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