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