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