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