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