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