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[] = " "; 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