2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
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"
17 #include "GrAuditTrail.h"
18 #include "GrContext.h"
19 #include "GrRenderTarget.h"
20 #include "SkGpuDevice.h"
23 #define SKDEBUGCANVAS_VERSION 1
24 #define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version"
25 #define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands"
26 #define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail"
28 class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
30 DebugPaintFilterCanvas(int width,
33 bool overrideFilterQuality,
34 SkFilterQuality quality)
35 : INHERITED(width, height)
36 , fOverdrawXfermode(overdrawViz ? SkOverdrawMode::Make() : nullptr)
37 , fOverrideFilterQuality(overrideFilterQuality)
38 , fFilterQuality(quality) {}
41 bool onFilter(SkTCopyOnFirstWrite<SkPaint>* paint, Type) const override {
43 if (nullptr != fOverdrawXfermode.get()) {
44 paint->writable()->setAntiAlias(false);
45 // TODO: replace overdraw mode with something else
46 // paint->writable()->setXfermode(fOverdrawXfermode);
49 if (fOverrideFilterQuality) {
50 paint->writable()->setFilterQuality(fFilterQuality);
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);
63 void onDrawShadowedPicture(const SkPicture* picture,
64 const SkMatrix* matrix,
66 const SkShadowParams& params) {
67 #ifdef SK_EXPERIMENTAL_SHADOWING
68 this->SkCanvas::onDrawShadowedPicture(picture, matrix, paint, params);
70 this->SkCanvas::onDrawPicture(picture, matrix, paint);
75 sk_sp<SkXfermode> fOverdrawXfermode;
77 bool fOverrideFilterQuality;
78 SkFilterQuality fFilterQuality;
80 typedef SkPaintFilterCanvas INHERITED;
83 SkDebugCanvas::SkDebugCanvas(int width, int height)
84 : INHERITED(width, height)
89 , fOverrideFilterQuality(false)
90 , fFilterQuality(kNone_SkFilterQuality)
91 , fClipVizColor(SK_ColorTRANSPARENT)
92 , fDrawGpuBatchBounds(false) {
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);
108 SkASSERT(!large.roundOut().isEmpty());
110 // call the base class' version to avoid adding a draw command
111 this->INHERITED::onClipRect(large, kReplace_Op, kHard_ClipEdgeStyle);
114 SkDebugCanvas::~SkDebugCanvas() {
115 fCommandVector.deleteAll();
118 void SkDebugCanvas::addDrawCommand(SkDrawCommand* command) {
119 fCommandVector.push(command);
122 void SkDebugCanvas::draw(SkCanvas* canvas) {
123 if (!fCommandVector.isEmpty()) {
124 this->drawTo(canvas, fCommandVector.count() - 1);
128 void SkDebugCanvas::applyUserTransform(SkCanvas* canvas) {
129 canvas->concat(fUserMatrix);
132 int SkDebugCanvas::getCommandAtPoint(int x, int y, int index) {
134 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
136 SkCanvas canvas(bitmap);
137 canvas.translate(SkIntToScalar(-x), SkIntToScalar(-y));
138 this->applyUserTransform(&canvas);
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);
147 if (prev != bitmap.getColor(0,0)) {
150 prev = bitmap.getColor(0,0);
155 class SkDebugClipVisitor : public SkCanvas::ClipVisitor {
157 SkDebugClipVisitor(SkCanvas* canvas) : fCanvas(canvas) {}
159 void clipRect(const SkRect& r, SkCanvas::ClipOp, bool doAA) override {
161 p.setColor(SK_ColorRED);
162 p.setStyle(SkPaint::kStroke_Style);
163 p.setAntiAlias(doAA);
164 fCanvas->drawRect(r, p);
166 void clipRRect(const SkRRect& rr, SkCanvas::ClipOp, bool doAA) override {
168 p.setColor(SK_ColorGREEN);
169 p.setStyle(SkPaint::kStroke_Style);
170 p.setAntiAlias(doAA);
171 fCanvas->drawRRect(rr, p);
173 void clipPath(const SkPath& path, SkCanvas::ClipOp, bool doAA) override {
175 p.setColor(SK_ColorBLUE);
176 p.setStyle(SkPaint::kStroke_Style);
177 p.setAntiAlias(doAA);
178 fCanvas->drawPath(path, p);
185 typedef SkCanvas::ClipVisitor INHERITED;
188 // set up the saveLayer commands so that the active ones
189 // return true in their 'active' method
190 void SkDebugCanvas::markActiveCommands(int index) {
191 fActiveLayers.rewind();
193 for (int i = 0; i < fCommandVector.count(); ++i) {
194 fCommandVector[i]->setActive(false);
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) {
206 for (int i = 0; i < fActiveLayers.count(); ++i) {
207 fActiveLayers[i]->setActive(true);
212 void SkDebugCanvas::drawTo(SkCanvas* canvas, int index, int m) {
213 SkASSERT(!fCommandVector.isEmpty());
214 SkASSERT(index < fCommandVector.count());
216 int saveCount = canvas->save();
218 SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()),
219 SkIntToScalar(canvas->getBaseLayerSize().height()));
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);
228 this->applyUserTransform(canvas);
230 if (fPaintFilterCanvas) {
231 fPaintFilterCanvas->addCanvas(canvas);
232 canvas = fPaintFilterCanvas.get();
237 this->markActiveCommands(index);
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);
248 for (int i = 0; i <= index; i++) {
249 if (i == index && fFilter) {
250 canvas->clear(0xAAFFFFFF);
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.
259 GrAuditTrail::AutoCollectBatches* acb = nullptr;
261 acb = new GrAuditTrail::AutoCollectBatches(at, i);
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
270 // All active culls draw their cull box
271 fCommandVector[i]->vizExecute(canvas);
273 fCommandVector[i]->setUserMatrix(fUserMatrix);
274 fCommandVector[i]->execute(canvas);
284 if (SkColorGetA(fClipVizColor) != 0) {
286 #define LARGE_COORD 1000000000
287 canvas->clipRect(SkRect::MakeLTRB(-LARGE_COORD, -LARGE_COORD, LARGE_COORD, LARGE_COORD),
288 SkCanvas::kReverseDifference_Op);
290 clipPaint.setColor(fClipVizColor);
291 canvas->drawPaint(clipPaint);
298 canvas->resetMatrix();
300 if (!windowRect.isEmpty()) {
301 SkRect r = windowRect;
302 r.outset(SK_Scalar1, SK_Scalar1);
303 canvas->clipRect(r, SkCanvas::kReplace_Op);
305 // visualize existing clips
306 SkDebugClipVisitor visitor(canvas);
308 canvas->replayClips(&visitor);
313 this->resetClipStackData();
314 const SkClipStack* clipStack = canvas->getClipStack();
315 SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
316 const SkClipStack::Element* element;
318 while ((element = iter.next())) {
319 SkClipStack::Element::Type type = element->getType();
321 if (type != SkClipStack::Element::kEmpty_Type) {
322 element->asPath(&operand);
324 SkCanvas::ClipOp elementOp = element->getOp();
325 this->addClipStackData(devPath, operand, elementOp);
326 if (elementOp == SkCanvas::kReplace_Op) {
329 Op(devPath, operand, (SkPathOp) elementOp, &devPath);
332 this->lastClipStackData(devPath);
334 fMatrix = canvas->getTotalMatrix();
335 if (!canvas->getClipDeviceBounds(&fClip)) {
339 canvas->restoreToCount(saveCount);
341 if (fPaintFilterCanvas) {
342 fPaintFilterCanvas->removeAll();
346 // draw any batches if required and issue a full reset onto GrAuditTrail
348 // just in case there is global reordering, we flush the canvas before querying
350 GrAuditTrail::AutoEnable ae(at);
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);
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();
363 // get the bounding boxes to draw
364 SkTArray<GrAuditTrail::BatchInfo> childrenBounds;
366 at->getBoundsByClientID(&childrenBounds, index);
368 // the client wants us to draw the mth batch
369 at->getBoundsByBatchListID(&childrenBounds.push_back(), m);
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
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);
386 paint.setColor(kOpBatchBounds);
388 canvas->drawRect(batch.fBounds, paint);
393 this->cleanupAuditTrail(canvas);
396 void SkDebugCanvas::deleteDrawCommandAt(int index) {
397 SkASSERT(index < fCommandVector.count());
398 delete fCommandVector[index];
399 fCommandVector.remove(index);
402 SkDrawCommand* SkDebugCanvas::getDrawCommandAt(int index) {
403 SkASSERT(index < fCommandVector.count());
404 return fCommandVector[index];
407 void SkDebugCanvas::setDrawCommandAt(int index, SkDrawCommand* command) {
408 SkASSERT(index < fCommandVector.count());
409 delete fCommandVector[index];
410 fCommandVector[index] = command;
413 const SkTDArray<SkString*>* SkDebugCanvas::getCommandInfo(int index) const {
414 SkASSERT(index < fCommandVector.count());
415 return fCommandVector[index]->Info();
418 bool SkDebugCanvas::getDrawCommandVisibilityAt(int index) {
419 SkASSERT(index < fCommandVector.count());
420 return fCommandVector[index]->isVisible();
423 const SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() const {
424 return fCommandVector;
427 SkTDArray <SkDrawCommand*>& SkDebugCanvas::getDrawCommands() {
428 return fCommandVector;
431 GrAuditTrail* SkDebugCanvas::getAuditTrail(SkCanvas* canvas) {
432 GrAuditTrail* at = nullptr;
434 GrContext* ctx = canvas->getGrContext();
436 at = ctx->getAuditTrail();
442 void SkDebugCanvas::drawAndCollectBatches(int n, SkCanvas* canvas) {
444 GrAuditTrail* at = this->getAuditTrail(canvas);
446 // loop over all of the commands and draw them, this is to collect reordering
448 for (int i = 0; i < this->getSize() && i <= n; i++) {
449 GrAuditTrail::AutoCollectBatches enable(at, i);
450 fCommandVector[i]->execute(canvas);
453 // in case there is some kind of global reordering
455 GrAuditTrail::AutoEnable ae(at);
462 void SkDebugCanvas::cleanupAuditTrail(SkCanvas* canvas) {
463 GrAuditTrail* at = this->getAuditTrail(canvas);
466 GrAuditTrail::AutoEnable ae(at);
472 Json::Value SkDebugCanvas::toJSON(UrlDataManager& urlDataManager, int n, SkCanvas* canvas) {
473 this->drawAndCollectBatches(n, canvas);
477 GrAuditTrail* at = this->getAuditTrail(canvas);
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);
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;
490 SkAssertResult(reader.parse(at->toJson(i).c_str(), parsedFromString));
492 commands[i][SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL] = parsedFromString;
496 this->cleanupAuditTrail(canvas);
497 result[SKDEBUGCANVAS_ATTRIBUTE_COMMANDS] = commands;
501 Json::Value SkDebugCanvas::toJSONBatchList(int n, SkCanvas* canvas) {
502 this->drawAndCollectBatches(n, canvas);
504 Json::Value parsedFromString;
506 GrAuditTrail* at = this->getAuditTrail(canvas);
508 GrAuditTrail::AutoManageBatchList enable(at);
510 SkAssertResult(reader.parse(at->toJson().c_str(), parsedFromString));
513 this->cleanupAuditTrail(canvas);
514 return parsedFromString;
517 void SkDebugCanvas::updatePaintFilterCanvas() {
518 if (!fOverdrawViz && !fOverrideFilterQuality) {
519 fPaintFilterCanvas.reset(nullptr);
523 const SkImageInfo info = this->imageInfo();
524 fPaintFilterCanvas.reset(new DebugPaintFilterCanvas(info.width(), info.height(), fOverdrawViz,
525 fOverrideFilterQuality, fFilterQuality));
528 void SkDebugCanvas::setOverdrawViz(bool overdrawViz) {
529 if (fOverdrawViz == overdrawViz) {
533 fOverdrawViz = overdrawViz;
534 this->updatePaintFilterCanvas();
537 void SkDebugCanvas::overrideTexFiltering(bool overrideTexFiltering, SkFilterQuality quality) {
538 if (fOverrideFilterQuality == overrideTexFiltering && fFilterQuality == quality) {
542 fOverrideFilterQuality = overrideTexFiltering;
543 fFilterQuality = quality;
544 this->updatePaintFilterCanvas();
547 void SkDebugCanvas::onClipPath(const SkPath& path, ClipOp op, ClipEdgeStyle edgeStyle) {
548 this->addDrawCommand(new SkClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
551 void SkDebugCanvas::onClipRect(const SkRect& rect, ClipOp op, ClipEdgeStyle edgeStyle) {
552 this->addDrawCommand(new SkClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
555 void SkDebugCanvas::onClipRRect(const SkRRect& rrect, ClipOp op, ClipEdgeStyle edgeStyle) {
556 this->addDrawCommand(new SkClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
559 void SkDebugCanvas::onClipRegion(const SkRegion& region, ClipOp op) {
560 this->addDrawCommand(new SkClipRegionCommand(region, op));
563 void SkDebugCanvas::didConcat(const SkMatrix& matrix) {
564 this->addDrawCommand(new SkConcatCommand(matrix));
565 this->INHERITED::didConcat(matrix);
568 void SkDebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
569 this->addDrawCommand(new SkDrawAnnotationCommand(rect, key, sk_ref_sp(value)));
572 void SkDebugCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar left,
573 SkScalar top, const SkPaint* paint) {
574 this->addDrawCommand(new SkDrawBitmapCommand(bitmap, left, top, paint));
577 void 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));
583 void SkDebugCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
584 const SkRect& dst, const SkPaint* paint) {
585 this->addDrawCommand(new SkDrawBitmapNineCommand(bitmap, center, dst, paint));
588 void SkDebugCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
589 const SkPaint* paint) {
590 this->addDrawCommand(new SkDrawImageCommand(image, left, top, paint));
593 void 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));
598 void SkDebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
599 this->addDrawCommand(new SkDrawOvalCommand(oval, paint));
602 void 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));
607 void SkDebugCanvas::onDrawPaint(const SkPaint& paint) {
608 this->addDrawCommand(new SkDrawPaintCommand(paint));
611 void SkDebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
612 this->addDrawCommand(new SkDrawPathCommand(path, paint));
615 void 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)));
624 void 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)));
634 void SkDebugCanvas::onDrawPoints(PointMode mode, size_t count,
635 const SkPoint pts[], const SkPaint& paint) {
636 this->addDrawCommand(new SkDrawPointsCommand(mode, count, pts, paint));
639 void SkDebugCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
640 const SkPaint& paint) {
641 this->addDrawCommand(new SkDrawPosTextCommand(text, byteLength, pos, paint));
644 void 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));
650 void SkDebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
651 // NOTE(chudy): Messing up when renamed to DrawRect... Why?
652 addDrawCommand(new SkDrawRectCommand(rect, paint));
655 void SkDebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
656 this->addDrawCommand(new SkDrawRRectCommand(rrect, paint));
659 void SkDebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
660 const SkPaint& paint) {
661 this->addDrawCommand(new SkDrawDRRectCommand(outer, inner, paint));
664 void 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));
669 void 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));
675 void 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));
680 void 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)),
686 void 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));
692 void 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));
700 void SkDebugCanvas::willRestore() {
701 this->addDrawCommand(new SkRestoreCommand());
702 this->INHERITED::willRestore();
705 void SkDebugCanvas::willSave() {
706 this->addDrawCommand(new SkSaveCommand());
707 this->INHERITED::willSave();
710 SkCanvas::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;
717 void SkDebugCanvas::didSetMatrix(const SkMatrix& matrix) {
718 this->addDrawCommand(new SkSetMatrixCommand(matrix));
719 this->INHERITED::didSetMatrix(matrix);
722 void SkDebugCanvas::didTranslateZ(SkScalar z) {
723 #ifdef SK_EXPERIMENTAL_SHADOWING
724 this->addDrawCommand(new SkTranslateZCommand(z));
725 this->INHERITED::didTranslateZ(z);
729 void SkDebugCanvas::toggleCommand(int index, bool toggle) {
730 SkASSERT(index < fCommandVector.count());
731 fCommandVector[index]->setVisible(toggle);
734 static const char* gFillTypeStrs[] = {
737 "kInverseWinding_FillType",
738 "kInverseEvenOdd_FillType"
741 static const char* gOpStrs[] = {
742 "kDifference_PathOp",
746 "kReverseDifference_PathOp",
749 static const char kHTML4SpaceIndent[] = " ";
751 void SkDebugCanvas::outputScalar(SkScalar num) {
752 if (num == (int) num) {
753 fClipStackData.appendf("%d", (int) num);
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') {
763 fClipStackData.appendf("%sf", str.c_str());
767 void 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(", ");
778 void SkDebugCanvas::outputPoints(const SkPoint* pts, int count) {
779 this->outputPointsCommon(pts, count);
780 fClipStackData.appendf(");<br>");
783 void SkDebugCanvas::outputConicPoints(const SkPoint* pts, SkScalar weight) {
784 this->outputPointsCommon(pts, 2);
785 fClipStackData.appendf(", ");
786 this->outputScalar(weight);
787 fClipStackData.appendf(");<br>");
790 void 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]);
799 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
801 case SkPath::kMove_Verb:
802 fClipStackData.appendf("%s%s.moveTo(", kHTML4SpaceIndent, pathName);
803 this->outputPoints(&pts[0], 1);
805 case SkPath::kLine_Verb:
806 fClipStackData.appendf("%s%s.lineTo(", kHTML4SpaceIndent, pathName);
807 this->outputPoints(&pts[1], 1);
809 case SkPath::kQuad_Verb:
810 fClipStackData.appendf("%s%s.quadTo(", kHTML4SpaceIndent, pathName);
811 this->outputPoints(&pts[1], 2);
813 case SkPath::kConic_Verb:
814 fClipStackData.appendf("%s%s.conicTo(", kHTML4SpaceIndent, pathName);
815 this->outputConicPoints(&pts[1], iter.conicWeight());
817 case SkPath::kCubic_Verb:
818 fClipStackData.appendf("%s%s.cubicTo(", kHTML4SpaceIndent, pathName);
819 this->outputPoints(&pts[1], 3);
821 case SkPath::kClose_Verb:
822 fClipStackData.appendf("%s%s.close();<br>", kHTML4SpaceIndent, pathName);
825 SkDEBUGFAIL("bad verb");
831 void 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;
837 fCalledAddStackData = false;
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;
850 bool SkDebugCanvas::lastClipStackData(const SkPath& devPath) {
851 if (fCalledAddStackData) {
852 fClipStackData.appendf("<br>");
853 addPathData(devPath, "pathOut");