SkDebugCanvas.cpp revision 2b2fc7db4f8a4a4d4a51d7eb4df6aa62e309928d
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 "SkOverdrawMode.h" 13#include "SkPaintFilterCanvas.h" 14#include "SkTextBlob.h" 15 16#if SK_SUPPORT_GPU 17#include "GrAuditTrail.h" 18#include "GrContext.h" 19#include "GrRenderTarget.h" 20#include "SkGpuDevice.h" 21#endif 22 23#define SKDEBUGCANVAS_VERSION 1 24#define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version" 25#define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands" 26#define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail" 27 28class DebugPaintFilterCanvas : public SkPaintFilterCanvas { 29public: 30 DebugPaintFilterCanvas(int width, 31 int height, 32 bool overdrawViz, 33 bool overrideFilterQuality, 34 SkFilterQuality quality) 35 : INHERITED(width, height) 36 , fOverdrawXfermode(overdrawViz ? SkOverdrawMode::Make() : nullptr) 37 , fOverrideFilterQuality(overrideFilterQuality) 38 , fFilterQuality(quality) {} 39 40protected: 41 bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type) const override { 42 if (*paint) { 43 if (nullptr != fOverdrawXfermode.get()) { 44 paint->writable()->setAntiAlias(false); 45 // TODO: replace overdraw mode with something else 46// paint->writable()->setXfermode(fOverdrawXfermode); 47 } 48 49 if (fOverrideFilterQuality) { 50 paint->writable()->setFilterQuality(fFilterQuality); 51 } 52 } 53 return true; 54 } 55 56 void onDrawPicture(const SkPicture* picture, 57 const SkMatrix* matrix, 58 const SkPaint* paint) override { 59 // We need to replay the picture onto this canvas in order to filter its internal paints. 60 this->SkCanvas::onDrawPicture(picture, matrix, paint); 61 } 62 63 void onDrawShadowedPicture(const SkPicture* picture, 64 const SkMatrix* matrix, 65 const SkPaint* paint, 66 const SkShadowParams& params) { 67#ifdef SK_EXPERIMENTAL_SHADOWING 68 this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint, params); 69#else 70 this->SkCanvas::onDrawPicture(picture, matrix, paint); 71#endif 72 } 73 74private: 75 sk_sp<SkXfermode> fOverdrawXfermode; 76 77 bool fOverrideFilterQuality; 78 SkFilterQuality fFilterQuality; 79 80 typedef SkPaintFilterCanvas INHERITED; 81}; 82 83SkDebugCanvas::SkDebugCanvas(int width, int height) 84 : INHERITED(width, height) 85 , fPicture(nullptr) 86 , fFilter(false) 87 , fMegaVizMode(false) 88 , fOverdrawViz(false) 89 , fOverrideFilterQuality(false) 90 , fFilterQuality(kNone_SkFilterQuality) 91 , fClipVizColor(SK_ColorTRANSPARENT) 92 , fDrawGpuBatchBounds(false) { 93 fUserMatrix.reset(); 94 95 // SkPicturePlayback uses the base-class' quickReject calls to cull clipped 96 // operations. This can lead to problems in the debugger which expects all 97 // the operations in the captured skp to appear in the debug canvas. To 98 // circumvent this we create a wide open clip here (an empty clip rect 99 // is not sufficient). 100 // Internally, the SkRect passed to clipRect is converted to an SkIRect and 101 // rounded out. The following code creates a nearly maximal rect that will 102 // not get collapsed by the coming conversions (Due to precision loss the 103 // inset has to be surprisingly large). 104 SkIRect largeIRect = SkIRect::MakeLargest(); 105 largeIRect.inset(1024, 1024); 106 SkRect large = SkRect::Make(largeIRect); 107#ifdef SK_DEBUG 108 SkASSERT(!large.roundOut().isEmpty()); 109#endif 110 // call the base class' version to avoid adding a draw command 111 this->INHERITED::onClipRect(large, kReplace_Op, kHard_ClipEdgeStyle); 112} 113 114SkDebugCanvas::~SkDebugCanvas() { 115 fCommandVector.deleteAll(); 116} 117 118void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) { 119 fCommandVector.push(command); 120} 121 122void SkDebugCanvas::draw(SkCanvas* canvas) { 123 if (!fCommandVector.isEmpty()) { 124 this->drawTo(canvas, fCommandVector.count() - 1); 125 } 126} 127 128void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) { 129 canvas->concat(fUserMatrix); 130} 131 132int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) { 133 SkBitmap bitmap; 134 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1)); 135 136 SkCanvas canvas(bitmap); 137 canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y)); 138 this->applyUserTransform(&canvas); 139 140 int layer = 0; 141 SkColor prev = bitmap.getColor(0,0); 142 for (int i = 0; i < index; i++) { 143 if (fCommandVector[i]->isVisible()) { 144 fCommandVector[i]->setUserMatrix(fUserMatrix); 145 fCommandVector[i]->execute(&canvas); 146 } 147 if (prev != bitmap.getColor(0,0)) { 148 layer = i; 149 } 150 prev = bitmap.getColor(0,0); 151 } 152 return layer; 153} 154 155class SkDebugClipVisitor : public SkCanvas::ClipVisitor { 156public: 157 SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {} 158 159 void clipRect(const SkRect& r, SkCanvas::ClipOp, bool doAA) override { 160 SkPaint p; 161 p.setColor(SK_ColorRED); 162 p.setStyle(SkPaint::kStroke_Style); 163 p.setAntiAlias(doAA); 164 fCanvas->drawRect(r, p); 165 } 166 void clipRRect(const SkRRect& rr, SkCanvas::ClipOp, bool doAA) override { 167 SkPaint p; 168 p.setColor(SK_ColorGREEN); 169 p.setStyle(SkPaint::kStroke_Style); 170 p.setAntiAlias(doAA); 171 fCanvas->drawRRect(rr, p); 172 } 173 void clipPath(const SkPath& path, SkCanvas::ClipOp, bool doAA) override { 174 SkPaint p; 175 p.setColor(SK_ColorBLUE); 176 p.setStyle(SkPaint::kStroke_Style); 177 p.setAntiAlias(doAA); 178 fCanvas->drawPath(path, p); 179 } 180 181protected: 182 SkCanvas* fCanvas; 183 184private: 185 typedef SkCanvas::ClipVisitor INHERITED; 186}; 187 188// set up the saveLayer commands so that the active ones 189// return true in their 'active' method 190void SkDebugCanvas::markActiveCommands(int index) { 191 fActiveLayers.rewind(); 192 193 for (int i = 0; i < fCommandVector.count(); ++i) { 194 fCommandVector[i]->setActive(false); 195 } 196 197 for (int i = 0; i < index; ++i) { 198 SkDrawCommand::Action result = fCommandVector[i]->action(); 199 if (SkDrawCommand::kPushLayer_Action == result) { 200 fActiveLayers.push(fCommandVector[i]); 201 } else if (SkDrawCommand::kPopLayer_Action == result) { 202 fActiveLayers.pop(); 203 } 204 } 205 206 for (int i = 0; i < fActiveLayers.count(); ++i) { 207 fActiveLayers[i]->setActive(true); 208 } 209 210} 211 212void SkDebugCanvas::drawTo(SkCanvas* canvas, int index, int m) { 213 SkASSERT(!fCommandVector.isEmpty()); 214 SkASSERT(index < fCommandVector.count()); 215 216 int saveCount = canvas->save(); 217 218 SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()), 219 SkIntToScalar(canvas->getBaseLayerSize().height())); 220 221 bool pathOpsMode = getAllowSimplifyClip(); 222 canvas->setAllowSimplifyClip(pathOpsMode); 223 canvas->clear(SK_ColorWHITE); 224 canvas->resetMatrix(); 225 if (!windowRect.isEmpty()) { 226 canvas->clipRect(windowRect, SkCanvas::kReplace_Op); 227 } 228 this->applyUserTransform(canvas); 229 230 if (fPaintFilterCanvas) { 231 fPaintFilterCanvas->addCanvas(canvas); 232 canvas = fPaintFilterCanvas.get(); 233 234 } 235 236 if (fMegaVizMode) { 237 this->markActiveCommands(index); 238 } 239 240#if SK_SUPPORT_GPU 241 // If we have a GPU backend we can also visualize the batching information 242 GrAuditTrail* at = nullptr; 243 if (fDrawGpuBatchBounds || m != -1) { 244 at = this->getAuditTrail(canvas); 245 } 246#endif 247 248 for (int i = 0; i <= index; i++) { 249 if (i == index && fFilter) { 250 canvas->clear(0xAAFFFFFF); 251 } 252 253#if SK_SUPPORT_GPU 254 // We need to flush any pending operations, or they might batch with commands below. 255 // Previous operations were not registered with the audit trail when they were 256 // created, so if we allow them to combine, the audit trail will fail to find them. 257 canvas->flush(); 258 259 GrAuditTrail::AutoCollectBatches* acb = nullptr; 260 if (at) { 261 acb = new GrAuditTrail::AutoCollectBatches(at, i); 262 } 263#endif 264 265 if (fCommandVector[i]->isVisible()) { 266 if (fMegaVizMode && fCommandVector[i]->active()) { 267 // "active" commands execute their visualization behaviors: 268 // All active saveLayers get replaced with saves so all draws go to the 269 // visible canvas. 270 // All active culls draw their cull box 271 fCommandVector[i]->vizExecute(canvas); 272 } else { 273 fCommandVector[i]->setUserMatrix(fUserMatrix); 274 fCommandVector[i]->execute(canvas); 275 } 276 } 277#if SK_SUPPORT_GPU 278 if (at && acb) { 279 delete acb; 280 } 281#endif 282 } 283 284 if (SkColorGetA(fClipVizColor) != 0) { 285 canvas->save(); 286 #define LARGE_COORD 1000000000 287 canvas->clipRect(SkRect::MakeLTRB(-LARGE_COORD, -LARGE_COORD, LARGE_COORD, LARGE_COORD), 288 SkCanvas::kReverseDifference_Op); 289 SkPaint clipPaint; 290 clipPaint.setColor(fClipVizColor); 291 canvas->drawPaint(clipPaint); 292 canvas->restore(); 293 } 294 295 if (fMegaVizMode) { 296 canvas->save(); 297 // nuke the CTM 298 canvas->resetMatrix(); 299 // turn off clipping 300 if (!windowRect.isEmpty()) { 301 SkRect r = windowRect; 302 r.outset(SK_Scalar1, SK_Scalar1); 303 canvas->clipRect(r, SkCanvas::kReplace_Op); 304 } 305 // visualize existing clips 306 SkDebugClipVisitor visitor(canvas); 307 308 canvas->replayClips(&visitor); 309 310 canvas->restore(); 311 } 312 if (pathOpsMode) { 313 this->resetClipStackData(); 314 const SkClipStack* clipStack = canvas->getClipStack(); 315 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart); 316 const SkClipStack::Element* element; 317 SkPath devPath; 318 while ((element = iter.next())) { 319 SkClipStack::Element::Type type = element->getType(); 320 SkPath operand; 321 if (type != SkClipStack::Element::kEmpty_Type) { 322 element->asPath(&operand); 323 } 324 SkCanvas::ClipOp elementOp = element->getOp(); 325 this->addClipStackData(devPath, operand, elementOp); 326 if (elementOp == SkCanvas::kReplace_Op) { 327 devPath = operand; 328 } else { 329 Op(devPath, operand, (SkPathOp) elementOp, &devPath); 330 } 331 } 332 this->lastClipStackData(devPath); 333 } 334 fMatrix = canvas->getTotalMatrix(); 335 if (!canvas->getClipDeviceBounds(&fClip)) { 336 fClip.setEmpty(); 337 } 338 339 canvas->restoreToCount(saveCount); 340 341 if (fPaintFilterCanvas) { 342 fPaintFilterCanvas->removeAll(); 343 } 344 345#if SK_SUPPORT_GPU 346 // draw any batches if required and issue a full reset onto GrAuditTrail 347 if (at) { 348 // just in case there is global reordering, we flush the canvas before querying 349 // GrAuditTrail 350 GrAuditTrail::AutoEnable ae(at); 351 canvas->flush(); 352 353 // we pick three colorblind-safe colors, 75% alpha 354 static const SkColor kTotalBounds = SkColorSetARGB(0xC0, 0x6A, 0x3D, 0x9A); 355 static const SkColor kOpBatchBounds = SkColorSetARGB(0xC0, 0xE3, 0x1A, 0x1C); 356 static const SkColor kOtherBatchBounds = SkColorSetARGB(0xC0, 0xFF, 0x7F, 0x00); 357 358 // get the render target of the top device so we can ignore batches drawn offscreen 359 SkBaseDevice* bd = canvas->getDevice_just_for_deprecated_compatibility_testing(); 360 SkGpuDevice* gbd = reinterpret_cast<SkGpuDevice*>(bd); 361 uint32_t rtID = gbd->accessDrawContext()->accessRenderTarget()->uniqueID(); 362 363 // get the bounding boxes to draw 364 SkTArray<GrAuditTrail::BatchInfo> childrenBounds; 365 if (m == -1) { 366 at->getBoundsByClientID(&childrenBounds, index); 367 } else { 368 // the client wants us to draw the mth batch 369 at->getBoundsByBatchListID(&childrenBounds.push_back(), m); 370 } 371 SkPaint paint; 372 paint.setStyle(SkPaint::kStroke_Style); 373 paint.setStrokeWidth(1); 374 for (int i = 0; i < childrenBounds.count(); i++) { 375 if (childrenBounds[i].fRenderTargetUniqueID != rtID) { 376 // offscreen draw, ignore for now 377 continue; 378 } 379 paint.setColor(kTotalBounds); 380 canvas->drawRect(childrenBounds[i].fBounds, paint); 381 for (int j = 0; j < childrenBounds[i].fBatches.count(); j++) { 382 const GrAuditTrail::BatchInfo::Batch& batch = childrenBounds[i].fBatches[j]; 383 if (batch.fClientID != index) { 384 paint.setColor(kOtherBatchBounds); 385 } else { 386 paint.setColor(kOpBatchBounds); 387 } 388 canvas->drawRect(batch.fBounds, paint); 389 } 390 } 391 } 392#endif 393 this->cleanupAuditTrail(canvas); 394} 395 396void SkDebugCanvas::deleteDrawCommandAt(int index) { 397 SkASSERT(index < fCommandVector.count()); 398 delete fCommandVector[index]; 399 fCommandVector.remove(index); 400} 401 402SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) { 403 SkASSERT(index < fCommandVector.count()); 404 return fCommandVector[index]; 405} 406 407void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) { 408 SkASSERT(index < fCommandVector.count()); 409 delete fCommandVector[index]; 410 fCommandVector[index] = command; 411} 412 413const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const { 414 SkASSERT(index < fCommandVector.count()); 415 return fCommandVector[index]->Info(); 416} 417 418bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) { 419 SkASSERT(index < fCommandVector.count()); 420 return fCommandVector[index]->isVisible(); 421} 422 423const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const { 424 return fCommandVector; 425} 426 427SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() { 428 return fCommandVector; 429} 430 431GrAuditTrail* SkDebugCanvas::getAuditTrail(SkCanvas* canvas) { 432 GrAuditTrail* at = nullptr; 433#if SK_SUPPORT_GPU 434 GrContext* ctx = canvas->getGrContext(); 435 if (ctx) { 436 at = ctx->getAuditTrail(); 437 } 438#endif 439 return at; 440} 441 442void SkDebugCanvas::drawAndCollectBatches(int n, SkCanvas* canvas) { 443#if SK_SUPPORT_GPU 444 GrAuditTrail* at = this->getAuditTrail(canvas); 445 if (at) { 446 // loop over all of the commands and draw them, this is to collect reordering 447 // information 448 for (int i = 0; i < this->getSize() && i <= n; i++) { 449 GrAuditTrail::AutoCollectBatches enable(at, i); 450 fCommandVector[i]->execute(canvas); 451 } 452 453 // in case there is some kind of global reordering 454 { 455 GrAuditTrail::AutoEnable ae(at); 456 canvas->flush(); 457 } 458 } 459#endif 460} 461 462void SkDebugCanvas::cleanupAuditTrail(SkCanvas* canvas) { 463 GrAuditTrail* at = this->getAuditTrail(canvas); 464 if (at) { 465#if SK_SUPPORT_GPU 466 GrAuditTrail::AutoEnable ae(at); 467 at->fullReset(); 468#endif 469 } 470} 471 472Json::Value SkDebugCanvas::toJSON(UrlDataManager& urlDataManager, int n, SkCanvas* canvas) { 473 this->drawAndCollectBatches(n, canvas); 474 475 // now collect json 476#if SK_SUPPORT_GPU 477 GrAuditTrail* at = this->getAuditTrail(canvas); 478#endif 479 Json::Value result = Json::Value(Json::objectValue); 480 result[SKDEBUGCANVAS_ATTRIBUTE_VERSION] = Json::Value(SKDEBUGCANVAS_VERSION); 481 Json::Value commands = Json::Value(Json::arrayValue); 482 for (int i = 0; i < this->getSize() && i <= n; i++) { 483 commands[i] = this->getDrawCommandAt(i)->toJSON(urlDataManager); 484#if SK_SUPPORT_GPU 485 if (at) { 486 // TODO if this is inefficient we could add a method to GrAuditTrail which takes 487 // a Json::Value and is only compiled in this file 488 Json::Value parsedFromString; 489 Json::Reader reader; 490 SkAssertResult(reader.parse(at->toJson(i).c_str(), parsedFromString)); 491 492 commands[i][SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL] = parsedFromString; 493 } 494#endif 495 } 496 this->cleanupAuditTrail(canvas); 497 result[SKDEBUGCANVAS_ATTRIBUTE_COMMANDS] = commands; 498 return result; 499} 500 501Json::Value SkDebugCanvas::toJSONBatchList(int n, SkCanvas* canvas) { 502 this->drawAndCollectBatches(n, canvas); 503 504 Json::Value parsedFromString; 505#if SK_SUPPORT_GPU 506 GrAuditTrail* at = this->getAuditTrail(canvas); 507 if (at) { 508 GrAuditTrail::AutoManageBatchList enable(at); 509 Json::Reader reader; 510 SkAssertResult(reader.parse(at->toJson().c_str(), parsedFromString)); 511 } 512#endif 513 this->cleanupAuditTrail(canvas); 514 return parsedFromString; 515} 516 517void SkDebugCanvas::updatePaintFilterCanvas() { 518 if (!fOverdrawViz && !fOverrideFilterQuality) { 519 fPaintFilterCanvas.reset(nullptr); 520 return; 521 } 522 523 const SkImageInfo info = this->imageInfo(); 524 fPaintFilterCanvas.reset(new DebugPaintFilterCanvas(info.width(), info.height(), fOverdrawViz, 525 fOverrideFilterQuality, fFilterQuality)); 526} 527 528void SkDebugCanvas::setOverdrawViz(bool overdrawViz) { 529 if (fOverdrawViz == overdrawViz) { 530 return; 531 } 532 533 fOverdrawViz = overdrawViz; 534 this->updatePaintFilterCanvas(); 535} 536 537void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) { 538 if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) { 539 return; 540 } 541 542 fOverrideFilterQuality = overrideTexFiltering; 543 fFilterQuality = quality; 544 this->updatePaintFilterCanvas(); 545} 546 547void SkDebugCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) { 548 this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle)); 549} 550 551void SkDebugCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) { 552 this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle)); 553} 554 555void SkDebugCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) { 556 this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle)); 557} 558 559void SkDebugCanvas::onClipRegion(const SkRegion& region, ClipOp op) { 560 this->addDrawCommand(new SkClipRegionCommand(region, op)); 561} 562 563void SkDebugCanvas::didConcat(const SkMatrix& matrix) { 564 this->addDrawCommand(new SkConcatCommand(matrix)); 565 this->INHERITED::didConcat(matrix); 566} 567 568void SkDebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { 569 this->addDrawCommand(new SkDrawAnnotationCommand(rect, key, sk_ref_sp(value))); 570} 571 572void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, 573 SkScalar top, const SkPaint* paint) { 574 this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint)); 575} 576 577void SkDebugCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, 578 const SkPaint* paint, SrcRectConstraint constraint) { 579 this->addDrawCommand(new SkDrawBitmapRectCommand(bitmap, src, dst, paint, 580 (SrcRectConstraint)constraint)); 581} 582 583void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, 584 const SkRect& dst, const SkPaint* paint) { 585 this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint)); 586} 587 588void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, 589 const SkPaint* paint) { 590 this->addDrawCommand(new SkDrawImageCommand(image, left, top, paint)); 591} 592 593void SkDebugCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, 594 const SkPaint* paint, SrcRectConstraint constraint) { 595 this->addDrawCommand(new SkDrawImageRectCommand(image, src, dst, paint, constraint)); 596} 597 598void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { 599 this->addDrawCommand(new SkDrawOvalCommand(oval, paint)); 600} 601 602void SkDebugCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle, 603 bool useCenter, const SkPaint& paint) { 604 this->addDrawCommand(new SkDrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint)); 605} 606 607void SkDebugCanvas::onDrawPaint(const SkPaint& paint) { 608 this->addDrawCommand(new SkDrawPaintCommand(paint)); 609} 610 611void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { 612 this->addDrawCommand(new SkDrawPathCommand(path, paint)); 613} 614 615void SkDebugCanvas::onDrawPicture(const SkPicture* picture, 616 const SkMatrix* matrix, 617 const SkPaint* paint) { 618 this->addDrawCommand(new SkBeginDrawPictureCommand(picture, matrix, paint)); 619 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); 620 picture->playback(this); 621 this->addDrawCommand(new SkEndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint))); 622} 623 624void SkDebugCanvas::onDrawShadowedPicture(const SkPicture* picture, 625 const SkMatrix* matrix, 626 const SkPaint* paint, 627 const SkShadowParams& params) { 628 this->addDrawCommand(new SkBeginDrawShadowedPictureCommand(picture, matrix, paint, params)); 629 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); 630 picture->playback(this); 631 this->addDrawCommand(new SkEndDrawShadowedPictureCommand(SkToBool(matrix) || SkToBool(paint))); 632} 633 634void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count, 635 const SkPoint pts[], const SkPaint& paint) { 636 this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint)); 637} 638 639void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], 640 const SkPaint& paint) { 641 this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint)); 642} 643 644void SkDebugCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[], 645 SkScalar constY, const SkPaint& paint) { 646 this->addDrawCommand( 647 new SkDrawPosTextHCommand(text, byteLength, xpos, constY, paint)); 648} 649 650void SkDebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { 651 // NOTE(chudy): Messing up when renamed to DrawRect... Why? 652 addDrawCommand(new SkDrawRectCommand(rect, paint)); 653} 654 655void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { 656 this->addDrawCommand(new SkDrawRRectCommand(rrect, paint)); 657} 658 659void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, 660 const SkPaint& paint) { 661 this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint)); 662} 663 664void SkDebugCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, 665 const SkPaint& paint) { 666 this->addDrawCommand(new SkDrawTextCommand(text, byteLength, x, y, paint)); 667} 668 669void SkDebugCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, 670 const SkMatrix* matrix, const SkPaint& paint) { 671 this->addDrawCommand( 672 new SkDrawTextOnPathCommand(text, byteLength, path, matrix, paint)); 673} 674 675void SkDebugCanvas::onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform xform[], 676 const SkRect* cull, const SkPaint& paint) { 677 this->addDrawCommand(new SkDrawTextRSXformCommand(text, byteLength, xform, cull, paint)); 678} 679 680void SkDebugCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, 681 const SkPaint& paint) { 682 this->addDrawCommand(new SkDrawTextBlobCommand(sk_ref_sp(const_cast<SkTextBlob*>(blob)), 683 x, y, paint)); 684} 685 686void SkDebugCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], 687 const SkPoint texCoords[4], SkXfermode* xmode, 688 const SkPaint& paint) { 689 this->addDrawCommand(new SkDrawPatchCommand(cubics, colors, texCoords, xmode, paint)); 690} 691 692void SkDebugCanvas::onDrawVertices(VertexMode vmode, int vertexCount, const SkPoint vertices[], 693 const SkPoint texs[], const SkColor colors[], 694 SkXfermode*, const uint16_t indices[], int indexCount, 695 const SkPaint& paint) { 696 this->addDrawCommand(new SkDrawVerticesCommand(vmode, vertexCount, vertices, 697 texs, colors, nullptr, indices, indexCount, paint)); 698} 699 700void SkDebugCanvas::willRestore() { 701 this->addDrawCommand(new SkRestoreCommand()); 702 this->INHERITED::willRestore(); 703} 704 705void SkDebugCanvas::willSave() { 706 this->addDrawCommand(new SkSaveCommand()); 707 this->INHERITED::willSave(); 708} 709 710SkCanvas::SaveLayerStrategy SkDebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { 711 this->addDrawCommand(new SkSaveLayerCommand(rec)); 712 (void)this->INHERITED::getSaveLayerStrategy(rec); 713 // No need for a full layer. 714 return kNoLayer_SaveLayerStrategy; 715} 716 717void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) { 718 this->addDrawCommand(new SkSetMatrixCommand(matrix)); 719 this->INHERITED::didSetMatrix(matrix); 720} 721 722void SkDebugCanvas::didTranslateZ(SkScalar z) { 723#ifdef SK_EXPERIMENTAL_SHADOWING 724 this->addDrawCommand(new SkTranslateZCommand(z)); 725 this->INHERITED::didTranslateZ(z); 726#endif 727} 728 729void SkDebugCanvas::toggleCommand(int index, bool toggle) { 730 SkASSERT(index < fCommandVector.count()); 731 fCommandVector[index]->setVisible(toggle); 732} 733 734static const char* gFillTypeStrs[] = { 735 "kWinding_FillType", 736 "kEvenOdd_FillType", 737 "kInverseWinding_FillType", 738 "kInverseEvenOdd_FillType" 739}; 740 741static const char* gOpStrs[] = { 742 "kDifference_PathOp", 743 "kIntersect_PathOp", 744 "kUnion_PathOp", 745 "kXor_PathOp", 746 "kReverseDifference_PathOp", 747}; 748 749static const char kHTML4SpaceIndent[] = " "; 750 751void SkDebugCanvas::outputScalar(SkScalar num) { 752 if (num == (int) num) { 753 fClipStackData.appendf("%d", (int) num); 754 } else { 755 SkString str; 756 str.printf("%1.9g", num); 757 int width = (int) str.size(); 758 const char* cStr = str.c_str(); 759 while (cStr[width - 1] == '0') { 760 --width; 761 } 762 str.resize(width); 763 fClipStackData.appendf("%sf", str.c_str()); 764 } 765} 766 767void SkDebugCanvas::outputPointsCommon(const SkPoint* pts, int count) { 768 for (int index = 0; index < count; ++index) { 769 this->outputScalar(pts[index].fX); 770 fClipStackData.appendf(", "); 771 this->outputScalar(pts[index].fY); 772 if (index + 1 < count) { 773 fClipStackData.appendf(", "); 774 } 775 } 776} 777 778void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) { 779 this->outputPointsCommon(pts, count); 780 fClipStackData.appendf(");<br>"); 781} 782 783void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) { 784 this->outputPointsCommon(pts, 2); 785 fClipStackData.appendf(", "); 786 this->outputScalar(weight); 787 fClipStackData.appendf(");<br>"); 788} 789 790void SkDebugCanvas::addPathData(const SkPath& path, const char* pathName) { 791 SkPath::RawIter iter(path); 792 SkPath::FillType fillType = path.getFillType(); 793 fClipStackData.appendf("%sSkPath %s;<br>", kHTML4SpaceIndent, pathName); 794 fClipStackData.appendf("%s%s.setFillType(SkPath::%s);<br>", kHTML4SpaceIndent, pathName, 795 gFillTypeStrs[fillType]); 796 iter.setPath(path); 797 uint8_t verb; 798 SkPoint pts[4]; 799 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { 800 switch (verb) { 801 case SkPath::kMove_Verb: 802 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName); 803 this->outputPoints(&pts[0], 1); 804 continue; 805 case SkPath::kLine_Verb: 806 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName); 807 this->outputPoints(&pts[1], 1); 808 break; 809 case SkPath::kQuad_Verb: 810 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName); 811 this->outputPoints(&pts[1], 2); 812 break; 813 case SkPath::kConic_Verb: 814 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName); 815 this->outputConicPoints(&pts[1], iter.conicWeight()); 816 break; 817 case SkPath::kCubic_Verb: 818 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName); 819 this->outputPoints(&pts[1], 3); 820 break; 821 case SkPath::kClose_Verb: 822 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName); 823 break; 824 default: 825 SkDEBUGFAIL("bad verb"); 826 return; 827 } 828 } 829} 830 831void SkDebugCanvas::addClipStackData(const SkPath& devPath, const SkPath& operand, 832 SkCanvas::ClipOp elementOp) { 833 if (elementOp == SkCanvas::kReplace_Op) { 834 if (!lastClipStackData(devPath)) { 835 fSaveDevPath = operand; 836 } 837 fCalledAddStackData = false; 838 } else { 839 fClipStackData.appendf("<br>static void test(skiatest::Reporter* reporter," 840 " const char* filename) {<br>"); 841 addPathData(fCalledAddStackData ? devPath : fSaveDevPath, "path"); 842 addPathData(operand, "pathB"); 843 fClipStackData.appendf("%stestPathOp(reporter, path, pathB, %s, filename);<br>", 844 kHTML4SpaceIndent, gOpStrs[elementOp]); 845 fClipStackData.appendf("}<br>"); 846 fCalledAddStackData = true; 847 } 848} 849 850bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) { 851 if (fCalledAddStackData) { 852 fClipStackData.appendf("<br>"); 853 addPathData(devPath, "pathOut"); 854 return true; 855 } 856 return false; 857} 858