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 width, int height) 18 : INHERITED(width, height) 19 , fPicture(NULL) 20 , fWidth(width) 21 , fHeight(height) 22 , fFilter(false) 23 , fMegaVizMode(false) 24 , fIndex(0) 25 , fOverdrawViz(false) 26 , fOverdrawFilter(NULL) 27 , fOverrideTexFiltering(false) 28 , fTexOverrideFilter(NULL) 29 , fOutstandingSaveCount(0) { 30 fUserMatrix.reset(); 31 32 // SkPicturePlayback uses the base-class' quickReject calls to cull clipped 33 // operations. This can lead to problems in the debugger which expects all 34 // the operations in the captured skp to appear in the debug canvas. To 35 // circumvent this we create a wide open clip here (an empty clip rect 36 // is not sufficient). 37 // Internally, the SkRect passed to clipRect is converted to an SkIRect and 38 // rounded out. The following code creates a nearly maximal rect that will 39 // not get collapsed by the coming conversions (Due to precision loss the 40 // inset has to be surprisingly large). 41 SkIRect largeIRect = SkIRect::MakeLargest(); 42 largeIRect.inset(1024, 1024); 43 SkRect large = SkRect::Make(largeIRect); 44 #ifdef SK_DEBUG 45 large.roundOut(&largeIRect); 46 SkASSERT(!largeIRect.isEmpty()); 47 #endif 48 // call the base class' version to avoid adding a draw command 49 this->INHERITED::onClipRect(large, SkRegion::kReplace_Op, kHard_ClipEdgeStyle); 50 } 51 52 SkDebugCanvas::~SkDebugCanvas() { 53 fCommandVector.deleteAll(); 54 SkSafeUnref(fOverdrawFilter); 55 SkSafeUnref(fTexOverrideFilter); 56 } 57 58 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) { 59 command->setOffset(this->getOpID()); 60 fCommandVector.push(command); 61 } 62 63 void SkDebugCanvas::draw(SkCanvas* canvas) { 64 if (!fCommandVector.isEmpty()) { 65 this->drawTo(canvas, fCommandVector.count() - 1); 66 } 67 } 68 69 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) { 70 canvas->concat(fUserMatrix); 71 } 72 73 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) { 74 SkBitmap bitmap; 75 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1)); 76 77 SkCanvas canvas(bitmap); 78 canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y)); 79 applyUserTransform(&canvas); 80 81 int layer = 0; 82 SkColor prev = bitmap.getColor(0,0); 83 for (int i = 0; i < index; i++) { 84 if (fCommandVector[i]->isVisible()) { 85 fCommandVector[i]->execute(&canvas); 86 } 87 if (prev != bitmap.getColor(0,0)) { 88 layer = i; 89 } 90 prev = bitmap.getColor(0,0); 91 } 92 return layer; 93 } 94 95 class OverdrawXfermode : public SkXfermode { 96 public: 97 virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const SK_OVERRIDE { 98 // This table encodes the color progression of the overdraw visualization 99 static const SkPMColor gTable[] = { 100 SkPackARGB32(0x00, 0x00, 0x00, 0x00), 101 SkPackARGB32(0xFF, 128, 158, 255), 102 SkPackARGB32(0xFF, 170, 185, 212), 103 SkPackARGB32(0xFF, 213, 195, 170), 104 SkPackARGB32(0xFF, 255, 192, 127), 105 SkPackARGB32(0xFF, 255, 185, 85), 106 SkPackARGB32(0xFF, 255, 165, 42), 107 SkPackARGB32(0xFF, 255, 135, 0), 108 SkPackARGB32(0xFF, 255, 95, 0), 109 SkPackARGB32(0xFF, 255, 50, 0), 110 SkPackARGB32(0xFF, 255, 0, 0) 111 }; 112 113 for (size_t i = 0; i < SK_ARRAY_COUNT(gTable)-1; ++i) { 114 if (gTable[i] == dst) { 115 return gTable[i+1]; 116 } 117 } 118 119 return gTable[SK_ARRAY_COUNT(gTable)-1]; 120 } 121 122 virtual Factory getFactory() const SK_OVERRIDE { return NULL; } 123 #ifndef SK_IGNORE_TO_STRING 124 virtual void toString(SkString* str) const { str->set("OverdrawXfermode"); } 125 #endif 126 }; 127 128 class SkOverdrawFilter : public SkDrawFilter { 129 public: 130 SkOverdrawFilter() { 131 fXferMode = SkNEW(OverdrawXfermode); 132 } 133 134 virtual ~SkOverdrawFilter() { 135 delete fXferMode; 136 } 137 138 virtual bool filter(SkPaint* p, Type) SK_OVERRIDE { 139 p->setXfermode(fXferMode); 140 return true; 141 } 142 143 protected: 144 SkXfermode* fXferMode; 145 146 private: 147 typedef SkDrawFilter INHERITED; 148 }; 149 150 // SkTexOverrideFilter modifies every paint to use the specified 151 // texture filtering mode 152 class SkTexOverrideFilter : public SkDrawFilter { 153 public: 154 SkTexOverrideFilter() : fFilterLevel(SkPaint::kNone_FilterLevel) { 155 } 156 157 void setFilterLevel(SkPaint::FilterLevel filterLevel) { 158 fFilterLevel = filterLevel; 159 } 160 161 virtual bool filter(SkPaint* p, Type) SK_OVERRIDE { 162 p->setFilterLevel(fFilterLevel); 163 return true; 164 } 165 166 protected: 167 SkPaint::FilterLevel fFilterLevel; 168 169 private: 170 typedef SkDrawFilter INHERITED; 171 }; 172 173 class SkDebugClipVisitor : public SkCanvas::ClipVisitor { 174 public: 175 SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {} 176 177 virtual void clipRect(const SkRect& r, SkRegion::Op, bool doAA) SK_OVERRIDE { 178 SkPaint p; 179 p.setColor(SK_ColorRED); 180 p.setStyle(SkPaint::kStroke_Style); 181 p.setAntiAlias(doAA); 182 fCanvas->drawRect(r, p); 183 } 184 virtual void clipRRect(const SkRRect& rr, SkRegion::Op, bool doAA) SK_OVERRIDE { 185 SkPaint p; 186 p.setColor(SK_ColorGREEN); 187 p.setStyle(SkPaint::kStroke_Style); 188 p.setAntiAlias(doAA); 189 fCanvas->drawRRect(rr, p); 190 } 191 virtual void clipPath(const SkPath& path, SkRegion::Op, bool doAA) SK_OVERRIDE { 192 SkPaint p; 193 p.setColor(SK_ColorBLUE); 194 p.setStyle(SkPaint::kStroke_Style); 195 p.setAntiAlias(doAA); 196 fCanvas->drawPath(path, p); 197 } 198 199 protected: 200 SkCanvas* fCanvas; 201 202 private: 203 typedef SkCanvas::ClipVisitor INHERITED; 204 }; 205 206 // set up the saveLayer commands so that the active ones 207 // return true in their 'active' method 208 void SkDebugCanvas::markActiveCommands(int index) { 209 fActiveLayers.rewind(); 210 fActiveCulls.rewind(); 211 212 for (int i = 0; i < fCommandVector.count(); ++i) { 213 fCommandVector[i]->setActive(false); 214 } 215 216 for (int i = 0; i < index; ++i) { 217 SkDrawCommand::Action result = fCommandVector[i]->action(); 218 if (SkDrawCommand::kPushLayer_Action == result) { 219 fActiveLayers.push(fCommandVector[i]); 220 } else if (SkDrawCommand::kPopLayer_Action == result) { 221 fActiveLayers.pop(); 222 } else if (SkDrawCommand::kPushCull_Action == result) { 223 fActiveCulls.push(fCommandVector[i]); 224 } else if (SkDrawCommand::kPopCull_Action == result) { 225 fActiveCulls.pop(); 226 } 227 } 228 229 for (int i = 0; i < fActiveLayers.count(); ++i) { 230 fActiveLayers[i]->setActive(true); 231 } 232 233 for (int i = 0; i < fActiveCulls.count(); ++i) { 234 fActiveCulls[i]->setActive(true); 235 } 236 } 237 238 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) { 239 SkASSERT(!fCommandVector.isEmpty()); 240 SkASSERT(index < fCommandVector.count()); 241 int i = 0; 242 243 bool pathOpsMode = getAllowSimplifyClip(); 244 canvas->setAllowSimplifyClip(pathOpsMode); 245 // This only works assuming the canvas and device are the same ones that 246 // were previously drawn into because they need to preserve all saves 247 // and restores. 248 // The visibility filter also requires a full re-draw - otherwise we can 249 // end up drawing the filter repeatedly. 250 if (fIndex < index && !fFilter && !fMegaVizMode && !pathOpsMode) { 251 i = fIndex + 1; 252 } else { 253 for (int j = 0; j < fOutstandingSaveCount; j++) { 254 canvas->restore(); 255 } 256 canvas->clear(SK_ColorTRANSPARENT); 257 canvas->resetMatrix(); 258 SkRect rect = SkRect::MakeWH(SkIntToScalar(fWidth), 259 SkIntToScalar(fHeight)); 260 canvas->clipRect(rect, SkRegion::kReplace_Op ); 261 applyUserTransform(canvas); 262 fOutstandingSaveCount = 0; 263 } 264 265 // The setting of the draw filter has to go here (rather than in 266 // SkRasterWidget) due to the canvas restores this class performs. 267 // Since the draw filter is stored in the layer stack if we 268 // call setDrawFilter on anything but the root layer odd things happen. 269 if (fOverdrawViz) { 270 if (NULL == fOverdrawFilter) { 271 fOverdrawFilter = new SkOverdrawFilter; 272 } 273 274 if (fOverdrawFilter != canvas->getDrawFilter()) { 275 canvas->setDrawFilter(fOverdrawFilter); 276 } 277 } else if (fOverrideTexFiltering) { 278 if (NULL == fTexOverrideFilter) { 279 fTexOverrideFilter = new SkTexOverrideFilter; 280 } 281 282 if (fTexOverrideFilter != canvas->getDrawFilter()) { 283 canvas->setDrawFilter(fTexOverrideFilter); 284 } 285 } else { 286 canvas->setDrawFilter(NULL); 287 } 288 289 if (fMegaVizMode) { 290 this->markActiveCommands(index); 291 } 292 293 for (; i <= index; i++) { 294 if (i == index && fFilter) { 295 SkPaint p; 296 p.setColor(0xAAFFFFFF); 297 canvas->save(); 298 canvas->resetMatrix(); 299 SkRect mask; 300 mask.set(SkIntToScalar(0), SkIntToScalar(0), 301 SkIntToScalar(fWidth), SkIntToScalar(fHeight)); 302 canvas->clipRect(mask, SkRegion::kReplace_Op, false); 303 canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0), 304 SkIntToScalar(fWidth), SkIntToScalar(fHeight), p); 305 canvas->restore(); 306 } 307 308 if (fCommandVector[i]->isVisible()) { 309 if (fMegaVizMode && fCommandVector[i]->active()) { 310 // "active" commands execute their visualization behaviors: 311 // All active saveLayers get replaced with saves so all draws go to the 312 // visible canvas. 313 // All active culls draw their cull box 314 fCommandVector[i]->vizExecute(canvas); 315 } else { 316 fCommandVector[i]->execute(canvas); 317 } 318 319 fCommandVector[i]->trackSaveState(&fOutstandingSaveCount); 320 } 321 } 322 323 if (fMegaVizMode) { 324 SkRect r = SkRect::MakeWH(SkIntToScalar(fWidth), SkIntToScalar(fHeight)); 325 r.outset(SK_Scalar1, SK_Scalar1); 326 327 canvas->save(); 328 // nuke the CTM 329 canvas->setMatrix(SkMatrix::I()); 330 // turn off clipping 331 canvas->clipRect(r, SkRegion::kReplace_Op); 332 333 // visualize existing clips 334 SkDebugClipVisitor visitor(canvas); 335 336 canvas->replayClips(&visitor); 337 338 canvas->restore(); 339 } 340 if (pathOpsMode) { 341 this->resetClipStackData(); 342 const SkClipStack* clipStack = canvas->getClipStack(); 343 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart); 344 const SkClipStack::Element* element; 345 SkPath devPath; 346 while ((element = iter.next())) { 347 SkClipStack::Element::Type type = element->getType(); 348 SkPath operand; 349 if (type != SkClipStack::Element::kEmpty_Type) { 350 element->asPath(&operand); 351 } 352 SkRegion::Op elementOp = element->getOp(); 353 this->addClipStackData(devPath, operand, elementOp); 354 if (elementOp == SkRegion::kReplace_Op) { 355 devPath = operand; 356 } else { 357 Op(devPath, operand, (SkPathOp) elementOp, &devPath); 358 } 359 } 360 this->lastClipStackData(devPath); 361 } 362 fMatrix = canvas->getTotalMatrix(); 363 if (!canvas->getClipDeviceBounds(&fClip)) { 364 fClip.setEmpty(); 365 } 366 fIndex = index; 367 } 368 369 void SkDebugCanvas::deleteDrawCommandAt(int index) { 370 SkASSERT(index < fCommandVector.count()); 371 delete fCommandVector[index]; 372 fCommandVector.remove(index); 373 } 374 375 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) { 376 SkASSERT(index < fCommandVector.count()); 377 return fCommandVector[index]; 378 } 379 380 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) { 381 SkASSERT(index < fCommandVector.count()); 382 delete fCommandVector[index]; 383 fCommandVector[index] = command; 384 } 385 386 SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) { 387 SkASSERT(index < fCommandVector.count()); 388 return fCommandVector[index]->Info(); 389 } 390 391 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) { 392 SkASSERT(index < fCommandVector.count()); 393 return fCommandVector[index]->isVisible(); 394 } 395 396 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const { 397 return fCommandVector; 398 } 399 400 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() { 401 return fCommandVector; 402 } 403 404 // TODO(chudy): Free command string memory. 405 SkTArray<SkString>* SkDebugCanvas::getDrawCommandsAsStrings() const { 406 SkTArray<SkString>* commandString = new SkTArray<SkString>(fCommandVector.count()); 407 if (!fCommandVector.isEmpty()) { 408 for (int i = 0; i < fCommandVector.count(); i ++) { 409 commandString->push_back() = fCommandVector[i]->toString(); 410 } 411 } 412 return commandString; 413 } 414 415 SkTDArray<size_t>* SkDebugCanvas::getDrawCommandOffsets() const { 416 SkTDArray<size_t>* commandOffsets = new SkTDArray<size_t>; 417 if (!fCommandVector.isEmpty()) { 418 for (int i = 0; i < fCommandVector.count(); i ++) { 419 *commandOffsets->push() = fCommandVector[i]->offset(); 420 } 421 } 422 return commandOffsets; 423 } 424 425 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkPaint::FilterLevel level) { 426 if (NULL == fTexOverrideFilter) { 427 fTexOverrideFilter = new SkTexOverrideFilter; 428 } 429 430 fOverrideTexFiltering = overrideTexFiltering; 431 fTexOverrideFilter->setFilterLevel(level); 432 } 433 434 void SkDebugCanvas::clear(SkColor color) { 435 this->addDrawCommand(new SkClearCommand(color)); 436 } 437 438 void SkDebugCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 439 this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle)); 440 } 441 442 void SkDebugCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 443 this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle)); 444 } 445 446 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) { 447 this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle)); 448 } 449 450 void SkDebugCanvas::onClipRegion(const SkRegion& region, SkRegion::Op op) { 451 this->addDrawCommand(new SkClipRegionCommand(region, op)); 452 } 453 454 void SkDebugCanvas::didConcat(const SkMatrix& matrix) { 455 switch (matrix.getType()) { 456 case SkMatrix::kTranslate_Mask: 457 this->addDrawCommand(new SkTranslateCommand(matrix.getTranslateX(), 458 matrix.getTranslateY())); 459 break; 460 case SkMatrix::kScale_Mask: 461 this->addDrawCommand(new SkScaleCommand(matrix.getScaleX(), 462 matrix.getScaleY())); 463 break; 464 default: 465 this->addDrawCommand(new SkConcatCommand(matrix)); 466 break; 467 } 468 469 this->INHERITED::didConcat(matrix); 470 } 471 472 void SkDebugCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left, 473 SkScalar top, const SkPaint* paint = NULL) { 474 this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint)); 475 } 476 477 void SkDebugCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, 478 const SkRect* src, const SkRect& dst, 479 const SkPaint* paint, 480 SkCanvas::DrawBitmapRectFlags flags) { 481 this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, flags)); 482 } 483 484 void SkDebugCanvas::drawBitmapMatrix(const SkBitmap& bitmap, 485 const SkMatrix& matrix, const SkPaint* paint) { 486 this->addDrawCommand(new SkDrawBitmapMatrixCommand(bitmap, matrix, paint)); 487 } 488 489 void SkDebugCanvas::drawBitmapNine(const SkBitmap& bitmap, 490 const SkIRect& center, const SkRect& dst, const SkPaint* paint) { 491 this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint)); 492 } 493 494 void SkDebugCanvas::drawData(const void* data, size_t length) { 495 this->addDrawCommand(new SkDrawDataCommand(data, length)); 496 } 497 498 void SkDebugCanvas::beginCommentGroup(const char* description) { 499 this->addDrawCommand(new SkBeginCommentGroupCommand(description)); 500 } 501 502 void SkDebugCanvas::addComment(const char* kywd, const char* value) { 503 this->addDrawCommand(new SkCommentCommand(kywd, value)); 504 } 505 506 void SkDebugCanvas::endCommentGroup() { 507 this->addDrawCommand(new SkEndCommentGroupCommand()); 508 } 509 510 void SkDebugCanvas::drawOval(const SkRect& oval, const SkPaint& paint) { 511 this->addDrawCommand(new SkDrawOvalCommand(oval, paint)); 512 } 513 514 void SkDebugCanvas::drawPaint(const SkPaint& paint) { 515 this->addDrawCommand(new SkDrawPaintCommand(paint)); 516 } 517 518 void SkDebugCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 519 this->addDrawCommand(new SkDrawPathCommand(path, paint)); 520 } 521 522 void SkDebugCanvas::onDrawPicture(const SkPicture* picture) { 523 this->addDrawCommand(new SkDrawPictureCommand(picture)); 524 } 525 526 void SkDebugCanvas::drawPoints(PointMode mode, size_t count, 527 const SkPoint pts[], const SkPaint& paint) { 528 this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint)); 529 } 530 531 void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], 532 const SkPaint& paint) { 533 this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint)); 534 } 535 536 void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], 537 SkScalar constY, const SkPaint& paint) { 538 this->addDrawCommand( 539 new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint)); 540 } 541 542 void SkDebugCanvas::drawRect(const SkRect& rect, const SkPaint& paint) { 543 // NOTE(chudy): Messing up when renamed to DrawRect... Why? 544 addDrawCommand(new SkDrawRectCommand(rect, paint)); 545 } 546 547 void SkDebugCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { 548 this->addDrawCommand(new SkDrawRRectCommand(rrect, paint)); 549 } 550 551 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, 552 const SkPaint& paint) { 553 this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint)); 554 } 555 556 void SkDebugCanvas::drawSprite(const SkBitmap& bitmap, int left, int top, 557 const SkPaint* paint = NULL) { 558 this->addDrawCommand(new SkDrawSpriteCommand(bitmap, left, top, paint)); 559 } 560 561 void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, 562 const SkPaint& paint) { 563 this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint)); 564 } 565 566 void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, 567 const SkMatrix* matrix, const SkPaint& paint) { 568 this->addDrawCommand( 569 new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint)); 570 } 571 572 void SkDebugCanvas::drawVertices(VertexMode vmode, int vertexCount, 573 const SkPoint vertices[], const SkPoint texs[], const SkColor colors[], 574 SkXfermode*, const uint16_t indices[], int indexCount, 575 const SkPaint& paint) { 576 this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices, 577 texs, colors, NULL, indices, indexCount, paint)); 578 } 579 580 void SkDebugCanvas::onPushCull(const SkRect& cullRect) { 581 this->addDrawCommand(new SkPushCullCommand(cullRect)); 582 } 583 584 void SkDebugCanvas::onPopCull() { 585 this->addDrawCommand(new SkPopCullCommand()); 586 } 587 588 void SkDebugCanvas::willRestore() { 589 this->addDrawCommand(new SkRestoreCommand()); 590 this->INHERITED::willRestore(); 591 } 592 593 void SkDebugCanvas::willSave(SaveFlags flags) { 594 this->addDrawCommand(new SkSaveCommand(flags)); 595 this->INHERITED::willSave(flags); 596 } 597 598 SkCanvas::SaveLayerStrategy SkDebugCanvas::willSaveLayer(const SkRect* bounds, const SkPaint* paint, 599 SaveFlags flags) { 600 this->addDrawCommand(new SkSaveLayerCommand(bounds, paint, flags)); 601 this->INHERITED::willSaveLayer(bounds, paint, flags); 602 // No need for a full layer. 603 return kNoLayer_SaveLayerStrategy; 604 } 605 606 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) { 607 this->addDrawCommand(new SkSetMatrixCommand(matrix)); 608 this->INHERITED::didSetMatrix(matrix); 609 } 610 611 void SkDebugCanvas::toggleCommand(int index, bool toggle) { 612 SkASSERT(index < fCommandVector.count()); 613 fCommandVector[index]->setVisible(toggle); 614 } 615 616 static const char* gFillTypeStrs[] = { 617 "kWinding_FillType", 618 "kEvenOdd_FillType", 619 "kInverseWinding_FillType", 620 "kInverseEvenOdd_FillType" 621 }; 622 623 static const char* gOpStrs[] = { 624 "kDifference_PathOp", 625 "kIntersect_PathOp", 626 "kUnion_PathOp", 627 "kXor_PathOp", 628 "kReverseDifference_PathOp", 629 }; 630 631 static const char kHTML4SpaceIndent[] = " "; 632 633 void SkDebugCanvas::outputScalar(SkScalar num) { 634 if (num == (int) num) { 635 fClipStackData.appendf("%d", (int) num); 636 } else { 637 SkString str; 638 str.printf("%1.9g", num); 639 int width = (int) str.size(); 640 const char* cStr = str.c_str(); 641 while (cStr[width - 1] == '0') { 642 --width; 643 } 644 str.resize(width); 645 fClipStackData.appendf("%sf", str.c_str()); 646 } 647 } 648 649 void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) { 650 for (int index = 0; index < count; ++index) { 651 this->outputScalar(pts[index].fX); 652 fClipStackData.appendf(", "); 653 this->outputScalar(pts[index].fY); 654 if (index + 1 < count) { 655 fClipStackData.appendf(", "); 656 } 657 } 658 } 659 660 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) { 661 this->outputPointsCommon(pts, count); 662 fClipStackData.appendf(");<br>"); 663 } 664 665 void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) { 666 this->outputPointsCommon(pts, 2); 667 fClipStackData.appendf(", "); 668 this->outputScalar(weight); 669 fClipStackData.appendf(");<br>"); 670 } 671 672 void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) { 673 SkPath::RawIter iter(path); 674 SkPath::FillType fillType = path.getFillType(); 675 fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName); 676 fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName, 677 gFillTypeStrs[fillType]); 678 iter.setPath(path); 679 uint8_t verb; 680 SkPoint pts[4]; 681 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 682 switch (verb) { 683 case SkPath::kMove_Verb: 684 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName); 685 this->outputPoints(&pts[0], 1); 686 continue; 687 case SkPath::kLine_Verb: 688 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName); 689 this->outputPoints(&pts[1], 1); 690 break; 691 case SkPath::kQuad_Verb: 692 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName); 693 this->outputPoints(&pts[1], 2); 694 break; 695 case SkPath::kConic_Verb: 696 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName); 697 this->outputConicPoints(&pts[1], iter.conicWeight()); 698 break; 699 case SkPath::kCubic_Verb: 700 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName); 701 this->outputPoints(&pts[1], 3); 702 break; 703 case SkPath::kClose_Verb: 704 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName); 705 break; 706 default: 707 SkDEBUGFAIL("bad verb"); 708 return; 709 } 710 } 711 } 712 713 void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand, 714 SkRegion::Op elementOp) { 715 if (elementOp == SkRegion::kReplace_Op) { 716 if (!lastClipStackData(devPath)) { 717 fSaveDevPath = operand; 718 } 719 fCalledAddStackData = false; 720 } else { 721 fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter," 722 " const char* filename) {<br>"); 723 addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path"); 724 addPathData(operand, "pathB"); 725 fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>", 726 kHTML4SpaceIndent, gOpStrs[elementOp]); 727 fClipStackData.appendf("}<br>"); 728 fCalledAddStackData = true; 729 } 730 } 731 732 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) { 733 if (fCalledAddStackData) { 734 fClipStackData.appendf("<br>"); 735 addPathData(devPath, "pathOut"); 736 return true; 737 } 738 return false; 739 } 740