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