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 "tools/debugger/DebugCanvas.h"
9
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkPicture.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkTextBlob.h"
15 #include "include/gpu/GrDirectContext.h"
16 #include "include/utils/SkPaintFilterCanvas.h"
17 #include "src/core/SkCanvasPriv.h"
18 #include "src/core/SkClipOpPriv.h"
19 #include "src/core/SkRectPriv.h"
20 #include "src/gpu/GrAuditTrail.h"
21 #include "src/gpu/GrRecordingContextPriv.h"
22 #include "src/gpu/GrSurfaceDrawContext.h"
23 #include "src/utils/SkJSONWriter.h"
24 #include "tools/debugger/DebugLayerManager.h"
25 #include "tools/debugger/DrawCommand.h"
26
27 #include <string>
28
29 #define SKDEBUGCANVAS_VERSION 1
30 #define SKDEBUGCANVAS_ATTRIBUTE_VERSION "version"
31 #define SKDEBUGCANVAS_ATTRIBUTE_COMMANDS "commands"
32 #define SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL "auditTrail"
33
34 namespace {
35 // Constants used in Annotations by Android for keeping track of layers
36 static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
37 static constexpr char kSurfaceID[] = "SurfaceID";
38 static constexpr char kAndroidClip[] = "AndroidDeviceClipRestriction";
39
40 static SkPath arrowHead = SkPath::Polygon({
41 { 0, 0},
42 { 6, -15},
43 { 0, -12},
44 {-6, -15},
45 }, true);
46
drawArrow(SkCanvas * canvas,const SkPoint & a,const SkPoint & b,const SkPaint & paint)47 void drawArrow(SkCanvas* canvas, const SkPoint& a, const SkPoint& b, const SkPaint& paint) {
48 canvas->translate(0.5, 0.5);
49 canvas->drawLine(a, b, paint);
50 canvas->save();
51 canvas->translate(b.fX, b.fY);
52 SkScalar angle = SkScalarATan2((b.fY - a.fY), b.fX - a.fX);
53 canvas->rotate(angle * 180 / SK_ScalarPI - 90);
54 // arrow head
55 canvas->drawPath(arrowHead, paint);
56 canvas->restore();
57 canvas->restore();
58 }
59 } // namespace
60
61 class DebugPaintFilterCanvas : public SkPaintFilterCanvas {
62 public:
DebugPaintFilterCanvas(SkCanvas * canvas)63 DebugPaintFilterCanvas(SkCanvas* canvas) : INHERITED(canvas) {}
64
65 protected:
onFilter(SkPaint & paint) const66 bool onFilter(SkPaint& paint) const override {
67 paint.setColor(SK_ColorRED);
68 paint.setAlpha(0x08);
69 paint.setBlendMode(SkBlendMode::kSrcOver);
70 return true;
71 }
72
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)73 void onDrawPicture(const SkPicture* picture,
74 const SkMatrix* matrix,
75 const SkPaint* paint) override {
76 // We need to replay the picture onto this canvas in order to filter its internal paints.
77 this->SkCanvas::onDrawPicture(picture, matrix, paint);
78 }
79
80 private:
81
82 using INHERITED = SkPaintFilterCanvas;
83 };
84
DebugCanvas(int width,int height)85 DebugCanvas::DebugCanvas(int width, int height)
86 : INHERITED(width, height)
87 , fOverdrawViz(false)
88 , fClipVizColor(SK_ColorTRANSPARENT)
89 , fDrawGpuOpBounds(false)
90 , fShowAndroidClip(false)
91 , fShowOrigin(false)
92 , fnextDrawPictureLayerId(-1)
93 , fnextDrawImageRectLayerId(-1)
94 , fAndroidClip(SkRect::MakeEmpty()) {
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 = SkRectPriv::MakeILarge();
105 largeIRect.inset(1024, 1024);
106 SkRect large = SkRect::Make(largeIRect);
107 #ifdef SK_DEBUG
108 SkASSERT(!large.roundOut().isEmpty());
109 #endif
110 // call the base class' version to avoid adding a draw command
111 this->INHERITED::onClipRect(large, SkClipOp::kIntersect, kHard_ClipEdgeStyle);
112 }
113
DebugCanvas(SkIRect bounds)114 DebugCanvas::DebugCanvas(SkIRect bounds)
115 : DebugCanvas(bounds.width(), bounds.height()) {}
116
~DebugCanvas()117 DebugCanvas::~DebugCanvas() { fCommandVector.deleteAll(); }
118
addDrawCommand(DrawCommand * command)119 void DebugCanvas::addDrawCommand(DrawCommand* command) { fCommandVector.push_back(command); }
120
draw(SkCanvas * canvas)121 void DebugCanvas::draw(SkCanvas* canvas) {
122 if (!fCommandVector.isEmpty()) {
123 this->drawTo(canvas, fCommandVector.count() - 1);
124 }
125 }
126
drawTo(SkCanvas * originalCanvas,int index,int m)127 void DebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) {
128 SkASSERT(!fCommandVector.isEmpty());
129 SkASSERT(index < fCommandVector.count());
130
131 int saveCount = originalCanvas->save();
132
133 SkRect windowRect = SkRect::MakeWH(SkIntToScalar(originalCanvas->getBaseLayerSize().width()),
134 SkIntToScalar(originalCanvas->getBaseLayerSize().height()));
135
136 originalCanvas->resetMatrix();
137 if (!windowRect.isEmpty()) {
138 originalCanvas->clipRect(windowRect, kReplace_SkClipOp);
139 }
140
141 DebugPaintFilterCanvas filterCanvas(originalCanvas);
142 SkCanvas* finalCanvas = fOverdrawViz ? &filterCanvas : originalCanvas;
143
144 auto dContext = GrAsDirectContext(finalCanvas->recordingContext());
145
146 // If we have a GPU backend we can also visualize the op information
147 GrAuditTrail* at = nullptr;
148 if (fDrawGpuOpBounds || m != -1) {
149 // The audit trail must be obtained from the original canvas.
150 at = this->getAuditTrail(originalCanvas);
151 }
152
153 for (int i = 0; i <= index; i++) {
154 GrAuditTrail::AutoCollectOps* acb = nullptr;
155 if (at) {
156 // We need to flush any pending operations, or they might combine with commands below.
157 // Previous operations were not registered with the audit trail when they were
158 // created, so if we allow them to combine, the audit trail will fail to find them.
159 if (dContext) {
160 dContext->flush();
161 }
162 acb = new GrAuditTrail::AutoCollectOps(at, i);
163 }
164 if (fCommandVector[i]->isVisible()) {
165 fCommandVector[i]->execute(finalCanvas);
166 }
167 if (at && acb) {
168 delete acb;
169 }
170 }
171
172 if (SkColorGetA(fClipVizColor) != 0) {
173 finalCanvas->save();
174 SkPaint clipPaint;
175 clipPaint.setColor(fClipVizColor);
176 finalCanvas->drawPaint(clipPaint);
177 finalCanvas->restore();
178 }
179
180 fMatrix = finalCanvas->getLocalToDevice();
181 fClip = finalCanvas->getDeviceClipBounds();
182 if (fShowOrigin) {
183 const SkPaint originXPaint = SkPaint({1.0, 0, 0, 1.0});
184 const SkPaint originYPaint = SkPaint({0, 1.0, 0, 1.0});
185 // Draw an origin cross at the origin before restoring to assist in visualizing the
186 // current matrix.
187 drawArrow(finalCanvas, {-50, 0}, {50, 0}, originXPaint);
188 drawArrow(finalCanvas, {0, -50}, {0, 50}, originYPaint);
189 }
190 finalCanvas->restoreToCount(saveCount);
191
192 if (fShowAndroidClip) {
193 // Draw visualization of android device clip restriction
194 SkPaint androidClipPaint;
195 androidClipPaint.setARGB(80, 255, 100, 0);
196 finalCanvas->drawRect(fAndroidClip, androidClipPaint);
197 }
198
199 // draw any ops if required and issue a full reset onto GrAuditTrail
200 if (at) {
201 // just in case there is global reordering, we flush the canvas before querying
202 // GrAuditTrail
203 GrAuditTrail::AutoEnable ae(at);
204 if (dContext) {
205 dContext->flush();
206 }
207
208 // we pick three colorblind-safe colors, 75% alpha
209 static const SkColor kTotalBounds = SkColorSetARGB(0xC0, 0x6A, 0x3D, 0x9A);
210 static const SkColor kCommandOpBounds = SkColorSetARGB(0xC0, 0xE3, 0x1A, 0x1C);
211 static const SkColor kOtherOpBounds = SkColorSetARGB(0xC0, 0xFF, 0x7F, 0x00);
212
213 // get the render target of the top device (from the original canvas) so we can ignore ops
214 // drawn offscreen
215 GrSurfaceDrawContext* sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(originalCanvas);
216 GrSurfaceProxy::UniqueID proxyID = sdc->asSurfaceProxy()->uniqueID();
217
218 // get the bounding boxes to draw
219 SkTArray<GrAuditTrail::OpInfo> childrenBounds;
220 if (m == -1) {
221 at->getBoundsByClientID(&childrenBounds, index);
222 } else {
223 // the client wants us to draw the mth op
224 at->getBoundsByOpsTaskID(&childrenBounds.push_back(), m);
225 }
226 // Shift the rects half a pixel, so they appear as exactly 1px thick lines.
227 finalCanvas->save();
228 finalCanvas->translate(0.5, -0.5);
229 SkPaint paint;
230 paint.setStyle(SkPaint::kStroke_Style);
231 paint.setStrokeWidth(1);
232 for (int i = 0; i < childrenBounds.count(); i++) {
233 if (childrenBounds[i].fProxyUniqueID != proxyID) {
234 // offscreen draw, ignore for now
235 continue;
236 }
237 paint.setColor(kTotalBounds);
238 finalCanvas->drawRect(childrenBounds[i].fBounds, paint);
239 for (int j = 0; j < childrenBounds[i].fOps.count(); j++) {
240 const GrAuditTrail::OpInfo::Op& op = childrenBounds[i].fOps[j];
241 if (op.fClientID != index) {
242 paint.setColor(kOtherOpBounds);
243 } else {
244 paint.setColor(kCommandOpBounds);
245 }
246 finalCanvas->drawRect(op.fBounds, paint);
247 }
248 }
249 finalCanvas->restore();
250 }
251 this->cleanupAuditTrail(originalCanvas);
252 }
253
deleteDrawCommandAt(int index)254 void DebugCanvas::deleteDrawCommandAt(int index) {
255 SkASSERT(index < fCommandVector.count());
256 delete fCommandVector[index];
257 fCommandVector.remove(index);
258 }
259
getDrawCommandAt(int index) const260 DrawCommand* DebugCanvas::getDrawCommandAt(int index) const {
261 SkASSERT(index < fCommandVector.count());
262 return fCommandVector[index];
263 }
264
getAuditTrail(SkCanvas * canvas)265 GrAuditTrail* DebugCanvas::getAuditTrail(SkCanvas* canvas) {
266 GrAuditTrail* at = nullptr;
267 auto ctx = canvas->recordingContext();
268 if (ctx) {
269 at = ctx->priv().auditTrail();
270 }
271 return at;
272 }
273
drawAndCollectOps(SkCanvas * canvas)274 void DebugCanvas::drawAndCollectOps(SkCanvas* canvas) {
275 GrAuditTrail* at = this->getAuditTrail(canvas);
276 if (at) {
277 // loop over all of the commands and draw them, this is to collect reordering
278 // information
279 for (int i = 0; i < this->getSize(); i++) {
280 GrAuditTrail::AutoCollectOps enable(at, i);
281 fCommandVector[i]->execute(canvas);
282 }
283
284 // in case there is some kind of global reordering
285 {
286 GrAuditTrail::AutoEnable ae(at);
287
288 auto dContext = GrAsDirectContext(canvas->recordingContext());
289 if (dContext) {
290 dContext->flush();
291 }
292 }
293 }
294 }
295
cleanupAuditTrail(SkCanvas * canvas)296 void DebugCanvas::cleanupAuditTrail(SkCanvas* canvas) {
297 GrAuditTrail* at = this->getAuditTrail(canvas);
298 if (at) {
299 GrAuditTrail::AutoEnable ae(at);
300 at->fullReset();
301 }
302 }
303
toJSON(SkJSONWriter & writer,UrlDataManager & urlDataManager,SkCanvas * canvas)304 void DebugCanvas::toJSON(SkJSONWriter& writer,
305 UrlDataManager& urlDataManager,
306 SkCanvas* canvas) {
307 this->drawAndCollectOps(canvas);
308
309 // now collect json
310 GrAuditTrail* at = this->getAuditTrail(canvas);
311 writer.appendS32(SKDEBUGCANVAS_ATTRIBUTE_VERSION, SKDEBUGCANVAS_VERSION);
312 writer.beginArray(SKDEBUGCANVAS_ATTRIBUTE_COMMANDS);
313
314 for (int i = 0; i < this->getSize(); i++) {
315 writer.beginObject(); // command
316 this->getDrawCommandAt(i)->toJSON(writer, urlDataManager);
317
318 if (at) {
319 writer.appendName(SKDEBUGCANVAS_ATTRIBUTE_AUDITTRAIL);
320 at->toJson(writer, i);
321 }
322 writer.endObject(); // command
323 }
324
325 writer.endArray(); // commands
326 this->cleanupAuditTrail(canvas);
327 }
328
toJSONOpsTask(SkJSONWriter & writer,SkCanvas * canvas)329 void DebugCanvas::toJSONOpsTask(SkJSONWriter& writer, SkCanvas* canvas) {
330 this->drawAndCollectOps(canvas);
331
332 GrAuditTrail* at = this->getAuditTrail(canvas);
333 if (at) {
334 GrAuditTrail::AutoManageOpsTask enable(at);
335 at->toJson(writer);
336 } else {
337 writer.beginObject();
338 writer.endObject();
339 }
340 this->cleanupAuditTrail(canvas);
341 }
342
setOverdrawViz(bool overdrawViz)343 void DebugCanvas::setOverdrawViz(bool overdrawViz) { fOverdrawViz = overdrawViz; }
344
onClipPath(const SkPath & path,SkClipOp op,ClipEdgeStyle edgeStyle)345 void DebugCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) {
346 this->addDrawCommand(new ClipPathCommand(path, op, kSoft_ClipEdgeStyle == edgeStyle));
347 }
348
onClipRect(const SkRect & rect,SkClipOp op,ClipEdgeStyle edgeStyle)349 void DebugCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) {
350 this->addDrawCommand(new ClipRectCommand(rect, op, kSoft_ClipEdgeStyle == edgeStyle));
351 }
352
onClipRRect(const SkRRect & rrect,SkClipOp op,ClipEdgeStyle edgeStyle)353 void DebugCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) {
354 this->addDrawCommand(new ClipRRectCommand(rrect, op, kSoft_ClipEdgeStyle == edgeStyle));
355 }
356
onClipRegion(const SkRegion & region,SkClipOp op)357 void DebugCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
358 this->addDrawCommand(new ClipRegionCommand(region, op));
359 }
360
onClipShader(sk_sp<SkShader> cs,SkClipOp op)361 void DebugCanvas::onClipShader(sk_sp<SkShader> cs, SkClipOp op) {
362 this->addDrawCommand(new ClipShaderCommand(std::move(cs), op));
363 }
364
didConcat44(const SkM44 & m)365 void DebugCanvas::didConcat44(const SkM44& m) {
366 this->addDrawCommand(new Concat44Command(m));
367 this->INHERITED::didConcat44(m);
368 }
369
didScale(SkScalar x,SkScalar y)370 void DebugCanvas::didScale(SkScalar x, SkScalar y) {
371 this->didConcat44(SkM44::Scale(x, y));
372 }
373
didTranslate(SkScalar x,SkScalar y)374 void DebugCanvas::didTranslate(SkScalar x, SkScalar y) {
375 this->didConcat44(SkM44::Translate(x, y));
376 }
377
onDrawAnnotation(const SkRect & rect,const char key[],SkData * value)378 void DebugCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) {
379 // Parse layer-releated annotations added in SkiaPipeline.cpp and RenderNodeDrawable.cpp
380 // the format of the annotations is <Indicator|RenderNodeId>
381 SkTArray<SkString> tokens;
382 SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
383 if (tokens.size() == 2) {
384 if (tokens[0].equals(kOffscreenLayerDraw)) {
385 // Indicates that the next drawPicture command contains the SkPicture to render the
386 // node at this id in an offscreen buffer.
387 fnextDrawPictureLayerId = std::stoi(tokens[1].c_str());
388 fnextDrawPictureDirtyRect = rect.roundOut();
389 return; // don't record it
390 } else if (tokens[0].equals(kSurfaceID)) {
391 // Indicates that the following drawImageRect should draw the offscreen buffer.
392 fnextDrawImageRectLayerId = std::stoi(tokens[1].c_str());
393 return; // don't record it
394 }
395 }
396 if (strcmp(kAndroidClip, key) == 0) {
397 // Store this frame's android device clip restriction for visualization later.
398 // This annotation stands in place of the androidFramework_setDeviceClipRestriction
399 // which is unrecordable.
400 fAndroidClip = rect;
401 }
402 this->addDrawCommand(new DrawAnnotationCommand(rect, key, sk_ref_sp(value)));
403 }
404
onDrawImage2(const SkImage * image,SkScalar left,SkScalar top,const SkSamplingOptions & sampling,const SkPaint * paint)405 void DebugCanvas::onDrawImage2(const SkImage* image,
406 SkScalar left,
407 SkScalar top,
408 const SkSamplingOptions& sampling,
409 const SkPaint* paint) {
410 this->addDrawCommand(new DrawImageCommand(image, left, top, sampling, paint));
411 }
412
onDrawImageLattice2(const SkImage * image,const Lattice & lattice,const SkRect & dst,SkFilterMode filter,const SkPaint * paint)413 void DebugCanvas::onDrawImageLattice2(const SkImage* image,
414 const Lattice& lattice,
415 const SkRect& dst,
416 SkFilterMode filter, // todo
417 const SkPaint* paint) {
418 this->addDrawCommand(new DrawImageLatticeCommand(image, lattice, dst, filter, paint));
419 }
420
onDrawImageRect2(const SkImage * image,const SkRect & src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)421 void DebugCanvas::onDrawImageRect2(const SkImage* image,
422 const SkRect& src,
423 const SkRect& dst,
424 const SkSamplingOptions& sampling,
425 const SkPaint* paint,
426 SrcRectConstraint constraint) {
427 if (fnextDrawImageRectLayerId != -1 && fLayerManager) {
428 // This drawImageRect command would have drawn the offscreen buffer for a layer.
429 // On Android, we recorded an SkPicture of the commands that drew to the layer.
430 // To render the layer as it would have looked on the frame this DebugCanvas draws, we need
431 // to call fLayerManager->getLayerAsImage(id). This must be done just before
432 // drawTo(command), since it depends on the index into the layer's commands
433 // (managed by fLayerManager)
434 // Instead of adding a DrawImageRectCommand, we need a deferred command, that when
435 // executed, will call drawImageRect(fLayerManager->getLayerAsImage())
436 this->addDrawCommand(new DrawImageRectLayerCommand(
437 fLayerManager, fnextDrawImageRectLayerId, fFrame, src, dst, sampling,
438 paint, constraint));
439 } else {
440 this->addDrawCommand(new DrawImageRectCommand(image, src, dst, sampling, paint, constraint));
441 }
442 // Reset expectation so next drawImageRect is not special.
443 fnextDrawImageRectLayerId = -1;
444 }
445
onDrawOval(const SkRect & oval,const SkPaint & paint)446 void DebugCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) {
447 this->addDrawCommand(new DrawOvalCommand(oval, paint));
448 }
449
onDrawArc(const SkRect & oval,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)450 void DebugCanvas::onDrawArc(const SkRect& oval,
451 SkScalar startAngle,
452 SkScalar sweepAngle,
453 bool useCenter,
454 const SkPaint& paint) {
455 this->addDrawCommand(new DrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint));
456 }
457
onDrawPaint(const SkPaint & paint)458 void DebugCanvas::onDrawPaint(const SkPaint& paint) {
459 this->addDrawCommand(new DrawPaintCommand(paint));
460 }
461
onDrawBehind(const SkPaint & paint)462 void DebugCanvas::onDrawBehind(const SkPaint& paint) {
463 this->addDrawCommand(new DrawBehindCommand(paint));
464 }
465
onDrawPath(const SkPath & path,const SkPaint & paint)466 void DebugCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) {
467 this->addDrawCommand(new DrawPathCommand(path, paint));
468 }
469
onDrawRegion(const SkRegion & region,const SkPaint & paint)470 void DebugCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) {
471 this->addDrawCommand(new DrawRegionCommand(region, paint));
472 }
473
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)474 void DebugCanvas::onDrawPicture(const SkPicture* picture,
475 const SkMatrix* matrix,
476 const SkPaint* paint) {
477 if (fnextDrawPictureLayerId != -1 && fLayerManager) {
478 fLayerManager->storeSkPicture(fnextDrawPictureLayerId, fFrame, sk_ref_sp(picture),
479 fnextDrawPictureDirtyRect);
480 } else {
481 this->addDrawCommand(new BeginDrawPictureCommand(picture, matrix, paint));
482 SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect());
483 picture->playback(this);
484 this->addDrawCommand(new EndDrawPictureCommand(SkToBool(matrix) || SkToBool(paint)));
485 }
486 fnextDrawPictureLayerId = -1;
487 }
488
onDrawPoints(PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)489 void DebugCanvas::onDrawPoints(PointMode mode,
490 size_t count,
491 const SkPoint pts[],
492 const SkPaint& paint) {
493 this->addDrawCommand(new DrawPointsCommand(mode, count, pts, paint));
494 }
495
onDrawRect(const SkRect & rect,const SkPaint & paint)496 void DebugCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) {
497 // NOTE(chudy): Messing up when renamed to DrawRect... Why?
498 addDrawCommand(new DrawRectCommand(rect, paint));
499 }
500
onDrawRRect(const SkRRect & rrect,const SkPaint & paint)501 void DebugCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
502 this->addDrawCommand(new DrawRRectCommand(rrect, paint));
503 }
504
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)505 void DebugCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) {
506 this->addDrawCommand(new DrawDRRectCommand(outer, inner, paint));
507 }
508
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)509 void DebugCanvas::onDrawTextBlob(const SkTextBlob* blob,
510 SkScalar x,
511 SkScalar y,
512 const SkPaint& paint) {
513 this->addDrawCommand(
514 new DrawTextBlobCommand(sk_ref_sp(const_cast<SkTextBlob*>(blob)), x, y, paint));
515 }
516
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode bmode,const SkPaint & paint)517 void DebugCanvas::onDrawPatch(const SkPoint cubics[12],
518 const SkColor colors[4],
519 const SkPoint texCoords[4],
520 SkBlendMode bmode,
521 const SkPaint& paint) {
522 this->addDrawCommand(new DrawPatchCommand(cubics, colors, texCoords, bmode, paint));
523 }
524
onDrawVerticesObject(const SkVertices * vertices,SkBlendMode bmode,const SkPaint & paint)525 void DebugCanvas::onDrawVerticesObject(const SkVertices* vertices,
526 SkBlendMode bmode,
527 const SkPaint& paint) {
528 this->addDrawCommand(
529 new DrawVerticesCommand(sk_ref_sp(const_cast<SkVertices*>(vertices)), bmode, paint));
530 }
531
onDrawAtlas2(const SkImage * image,const SkRSXform xform[],const SkRect tex[],const SkColor colors[],int count,SkBlendMode bmode,const SkSamplingOptions & sampling,const SkRect * cull,const SkPaint * paint)532 void DebugCanvas::onDrawAtlas2(const SkImage* image,
533 const SkRSXform xform[],
534 const SkRect tex[],
535 const SkColor colors[],
536 int count,
537 SkBlendMode bmode,
538 const SkSamplingOptions& sampling,
539 const SkRect* cull,
540 const SkPaint* paint) {
541 this->addDrawCommand(
542 new DrawAtlasCommand(image, xform, tex, colors, count, bmode, sampling, cull, paint));
543 }
544
onDrawShadowRec(const SkPath & path,const SkDrawShadowRec & rec)545 void DebugCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
546 this->addDrawCommand(new DrawShadowCommand(path, rec));
547 }
548
onDrawDrawable(SkDrawable * drawable,const SkMatrix * matrix)549 void DebugCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
550 this->addDrawCommand(new DrawDrawableCommand(drawable, matrix));
551 }
552
onDrawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],QuadAAFlags aa,const SkColor4f & color,SkBlendMode mode)553 void DebugCanvas::onDrawEdgeAAQuad(const SkRect& rect,
554 const SkPoint clip[4],
555 QuadAAFlags aa,
556 const SkColor4f& color,
557 SkBlendMode mode) {
558 this->addDrawCommand(new DrawEdgeAAQuadCommand(rect, clip, aa, color, mode));
559 }
560
onDrawEdgeAAImageSet2(const ImageSetEntry set[],int count,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)561 void DebugCanvas::onDrawEdgeAAImageSet2(const ImageSetEntry set[],
562 int count,
563 const SkPoint dstClips[],
564 const SkMatrix preViewMatrices[],
565 const SkSamplingOptions& sampling,
566 const SkPaint* paint,
567 SrcRectConstraint constraint) {
568 this->addDrawCommand(new DrawEdgeAAImageSetCommand(
569 set, count, dstClips, preViewMatrices, sampling, paint, constraint));
570 }
571
willRestore()572 void DebugCanvas::willRestore() {
573 this->addDrawCommand(new RestoreCommand());
574 this->INHERITED::willRestore();
575 }
576
willSave()577 void DebugCanvas::willSave() {
578 this->addDrawCommand(new SaveCommand());
579 this->INHERITED::willSave();
580 }
581
getSaveLayerStrategy(const SaveLayerRec & rec)582 SkCanvas::SaveLayerStrategy DebugCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) {
583 this->addDrawCommand(new SaveLayerCommand(rec));
584 (void)this->INHERITED::getSaveLayerStrategy(rec);
585 // No need for a full layer.
586 return kNoLayer_SaveLayerStrategy;
587 }
588
onDoSaveBehind(const SkRect * subset)589 bool DebugCanvas::onDoSaveBehind(const SkRect* subset) {
590 // TODO
591 return false;
592 }
593
didSetM44(const SkM44 & matrix)594 void DebugCanvas::didSetM44(const SkM44& matrix) {
595 this->addDrawCommand(new SetM44Command(matrix));
596 this->INHERITED::didSetM44(matrix);
597 }
598
toggleCommand(int index,bool toggle)599 void DebugCanvas::toggleCommand(int index, bool toggle) {
600 SkASSERT(index < fCommandVector.count());
601 fCommandVector[index]->setVisible(toggle);
602 }
603
getImageIdToCommandMap(UrlDataManager & udm) const604 std::map<int, std::vector<int>> DebugCanvas::getImageIdToCommandMap(UrlDataManager& udm) const {
605 // map from image ids to list of commands that reference them.
606 std::map<int, std::vector<int>> m;
607
608 for (int i = 0; i < this->getSize(); i++) {
609 const DrawCommand* command = this->getDrawCommandAt(i);
610 int imageIndex = -1;
611 // this is not an exaustive list of where images can be used, they show up in paints too.
612 switch (command->getOpType()) {
613 case DrawCommand::OpType::kDrawImage_OpType: {
614 imageIndex = static_cast<const DrawImageCommand*>(command)->imageId(udm);
615 break;
616 }
617 case DrawCommand::OpType::kDrawImageRect_OpType: {
618 imageIndex = static_cast<const DrawImageRectCommand*>(command)->imageId(udm);
619 break;
620 }
621 case DrawCommand::OpType::kDrawImageLattice_OpType: {
622 imageIndex = static_cast<const DrawImageLatticeCommand*>(command)->imageId(udm);
623 break;
624 }
625 default: break;
626 }
627 if (imageIndex >= 0) {
628 m[imageIndex].push_back(i);
629 }
630 }
631 return m;
632 }
633