blob: 4ee0ca9c6d58b9af4de90ad5e179f0533ee77b47 [file] [log] [blame]
Brian Salomon06c9e292021-04-29 14:10:23 -04001/*
2 * Copyright 2021 Google LLC
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/MSKPPlayer.h"
9
10#include "include/core/SkCanvas.h"
11#include "include/core/SkCanvasVirtualEnforcer.h"
12#include "include/core/SkPicture.h"
13#include "include/core/SkPictureRecorder.h"
14#include "include/core/SkSurface.h"
Brian Salomon31fddc32021-04-30 13:08:55 -040015#include "include/gpu/GrDirectContext.h"
Kevin Lubickdc6cc022023-01-13 11:24:27 -050016#include "include/private/base/SkTArray.h"
Brian Salomon06c9e292021-04-29 14:10:23 -040017#include "include/utils/SkNoDrawCanvas.h"
Kevin Lubick1b3aa8b2023-01-19 14:03:31 -050018#include "src/base/SkTLazy.h"
Brian Salomon06c9e292021-04-29 14:10:23 -040019#include "src/core/SkCanvasPriv.h"
Brian Osmana8c24d42023-02-27 11:23:54 -050020#include "src/core/SkStringUtils.h"
Kevin Lubick68de6e32023-10-13 11:19:03 -040021#include "include/docs/SkMultiPictureDocument.h"
Brian Salomon06c9e292021-04-29 14:10:23 -040022#include "tools/SkSharingProc.h"
23
Herb Derbyd7a008392023-03-02 15:36:17 -050024using namespace skia_private;
25
Brian Salomon06c9e292021-04-29 14:10:23 -040026///////////////////////////////////////////////////////////////////////////////
27
28// Base Cmd struct.
29struct MSKPPlayer::Cmd {
30 virtual ~Cmd() = default;
Brian Salomon33ec61132021-05-12 12:03:02 -040031 virtual bool isFullRedraw(SkCanvas*) const = 0;
Brian Salomon06c9e292021-04-29 14:10:23 -040032 virtual void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const = 0;
Brian Salomon33ec61132021-05-12 12:03:02 -040033 // If this command draws another layer, return it's ID. Otherwise, -1.
34 virtual int layerID() const { return -1; }
Brian Salomon06c9e292021-04-29 14:10:23 -040035};
36
37// Draws a SkPicture.
38struct MSKPPlayer::PicCmd : Cmd {
39 sk_sp<SkPicture> fContent;
Brian Salomon33ec61132021-05-12 12:03:02 -040040 SkIRect fClipRect = SkIRect::MakeEmpty(); // clip for picture (no clip if empty).
41
42 bool isFullRedraw(SkCanvas* canvas) const override {
43 if (fClipRect.isEmpty()) {
44 return false;
45 }
46 return fClipRect.contains(SkIRect::MakeSize(canvas->getBaseLayerSize()));
47 }
Brian Salomon06c9e292021-04-29 14:10:23 -040048
49 void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override {
Brian Salomon33ec61132021-05-12 12:03:02 -040050 if (!fClipRect.isEmpty()) {
51 canvas->save();
52 canvas->clipIRect(fClipRect);
53 }
Brian Salomon06c9e292021-04-29 14:10:23 -040054 canvas->drawPicture(fContent.get());
Brian Salomon33ec61132021-05-12 12:03:02 -040055 if (!fClipRect.isEmpty()) {
56 canvas->restore();
57 }
Brian Salomon06c9e292021-04-29 14:10:23 -040058 }
59};
60
61// Draws another layer. Stores the ID of the layer to draw and what command index on that
62// layer should be current when the layer is drawn. The layer contents are updated to the
63// stored command index before the layer is drawn.
64struct MSKPPlayer::DrawLayerCmd : Cmd {
65 int fLayerId;
66 size_t fLayerCmdCnt;
67 SkRect fSrcRect;
68 SkRect fDstRect;
69 SkSamplingOptions fSampling;
70 SkCanvas::SrcRectConstraint fConstraint;
71 SkTLazy<SkPaint> fPaint;
72
Brian Salomon33ec61132021-05-12 12:03:02 -040073 bool isFullRedraw(SkCanvas* canvas) const override { return false; }
Brian Salomon06c9e292021-04-29 14:10:23 -040074 void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override;
Brian Salomon33ec61132021-05-12 12:03:02 -040075 int layerID() const override { return fLayerId; }
Brian Salomon06c9e292021-04-29 14:10:23 -040076};
77
78void MSKPPlayer::DrawLayerCmd::draw(SkCanvas* canvas,
79 const LayerMap& layerMap,
80 LayerStateMap* layerStateMap) const {
Brian Osman68556bc2021-07-30 15:24:22 -040081 const LayerCmds& layer = layerMap.at(fLayerId);
Brian Salomon06c9e292021-04-29 14:10:23 -040082 LayerState* layerState = &(*layerStateMap)[fLayerId];
83 if (!layerState->fSurface) {
84 layerState->fCurrCmd = 0;
Brian Salomon31fddc32021-04-30 13:08:55 -040085 layerState->fSurface = MSKPPlayer::MakeSurfaceForLayer(layer, canvas);
Brian Salomon06c9e292021-04-29 14:10:23 -040086 if (!layerState->fSurface) {
87 SkDebugf("Couldn't create surface for layer");
88 return;
89 }
90 }
91 size_t cmd = layerState->fCurrCmd;
92 if (cmd > fLayerCmdCnt) {
93 // If the layer contains contents from later commands then replay from the beginning.
94 cmd = 0;
95 }
96 SkCanvas* layerCanvas = layerState->fSurface->getCanvas();
Brian Salomon33ec61132021-05-12 12:03:02 -040097 // Check if there is a full redraw between cmd and fLayerCmdCnt and if so jump to it and ensure
98 // we clear the canvas if starting from a full redraw.
99 for (size_t checkCmd = fLayerCmdCnt - 1; checkCmd > cmd; --checkCmd) {
100 if (layer.fCmds[checkCmd]->isFullRedraw(layerCanvas)) {
101 cmd = checkCmd;
102 break;
103 }
104 }
Brian Salomon06c9e292021-04-29 14:10:23 -0400105 for (; cmd < fLayerCmdCnt; ++cmd) {
Brian Salomon33ec61132021-05-12 12:03:02 -0400106 if (cmd == 0 || layer.fCmds[cmd]->isFullRedraw(layerCanvas)) {
107 layerState->fSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
108 }
Brian Salomon06c9e292021-04-29 14:10:23 -0400109 layer.fCmds[cmd]->draw(layerCanvas, layerMap, layerStateMap);
110 }
Brian Salomon33ec61132021-05-12 12:03:02 -0400111 layerState->fCurrCmd = fLayerCmdCnt;
Brian Salomon06c9e292021-04-29 14:10:23 -0400112 const SkPaint* paint = fPaint.isValid() ? fPaint.get() : nullptr;
113 canvas->drawImageRect(layerState->fSurface->makeImageSnapshot(),
114 fSrcRect,
115 fDstRect,
116 fSampling,
117 paint,
118 fConstraint);
119}
120
121///////////////////////////////////////////////////////////////////////////////
122
123class MSKPPlayer::CmdRecordCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
124public:
Brian Osman68556bc2021-07-30 15:24:22 -0400125 CmdRecordCanvas(LayerCmds* dst, LayerMap* offscreenLayers, const SkIRect* clipRect = nullptr)
Brian Salomon06c9e292021-04-29 14:10:23 -0400126 : fDst(dst), fOffscreenLayers(offscreenLayers) {
Brian Salomon33ec61132021-05-12 12:03:02 -0400127 if (clipRect) {
128 fClipRect = *clipRect;
129 }
Brian Salomon06c9e292021-04-29 14:10:23 -0400130 fRecorder.beginRecording(SkRect::Make(dst->fDimensions));
131 }
132 ~CmdRecordCanvas() override { this->recordPicCmd(); }
133
134protected:
135 void onDrawPaint(const SkPaint& paint) override {
136 fRecorder.getRecordingCanvas()->drawPaint(paint);
137 }
138
139 void onDrawBehind(const SkPaint& paint) override {
140 SkCanvasPriv::DrawBehind(fRecorder.getRecordingCanvas(), paint);
141 }
142
143 void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
144 fRecorder.getRecordingCanvas()->drawRect(rect, paint);
145 }
146
147 void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
148 fRecorder.getRecordingCanvas()->drawRRect(rrect, paint);
149 }
150
151 void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
152 fRecorder.getRecordingCanvas()->drawDRRect(outer, inner, paint);
153 }
154
155 void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
156 fRecorder.getRecordingCanvas()->drawOval(rect, paint);
157 }
158
159 void onDrawArc(const SkRect& rect,
160 SkScalar startAngle,
161 SkScalar sweepAngle,
162 bool useCenter,
163 const SkPaint& paint) override {
164 fRecorder.getRecordingCanvas()->drawArc(rect, startAngle, sweepAngle, useCenter, paint);
165 }
166
167 void onDrawPath(const SkPath& path, const SkPaint& paint) override {
168 fRecorder.getRecordingCanvas()->drawPath(path, paint);
169 }
170
171 void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
172 fRecorder.getRecordingCanvas()->drawRegion(region, paint);
173 }
174
175 void onDrawTextBlob(const SkTextBlob* blob,
176 SkScalar x,
177 SkScalar y,
178 const SkPaint& paint) override {
179 fRecorder.getRecordingCanvas()->drawTextBlob(blob, x, y, paint);
180 }
181
182 void onDrawPatch(const SkPoint cubics[12],
183 const SkColor colors[4],
184 const SkPoint texCoords[4],
185 SkBlendMode mode,
186 const SkPaint& paint) override {
187 fRecorder.getRecordingCanvas()->drawPatch(cubics, colors, texCoords, mode, paint);
188 }
189
190 void onDrawPoints(SkCanvas::PointMode mode,
191 size_t count,
192 const SkPoint pts[],
193 const SkPaint& paint) override {
194 fRecorder.getRecordingCanvas()->drawPoints(mode, count, pts, paint);
195 }
196
197 void onDrawImage2(const SkImage* image,
198 SkScalar dx,
199 SkScalar dy,
200 const SkSamplingOptions& sampling,
201 const SkPaint* paint) override {
202 fRecorder.getRecordingCanvas()->drawImage(image, dx, dy, sampling, paint);
203 }
204
205 void onDrawImageRect2(const SkImage* image,
206 const SkRect& src,
207 const SkRect& dst,
208 const SkSamplingOptions& sampling,
209 const SkPaint* paint,
210 SrcRectConstraint constraint) override {
211 if (fNextDrawImageFromLayerID != -1) {
212 this->recordPicCmd();
213 auto drawLayer = std::make_unique<DrawLayerCmd>();
214 drawLayer->fLayerId = fNextDrawImageFromLayerID;
215 drawLayer->fLayerCmdCnt = fOffscreenLayers->at(fNextDrawImageFromLayerID).fCmds.size();
216 drawLayer->fSrcRect = src;
217 drawLayer->fDstRect = dst;
218 drawLayer->fSampling = sampling;
219 drawLayer->fConstraint = constraint;
220 if (paint) {
221 drawLayer->fPaint.init(*paint);
222 }
223 fDst->fCmds.push_back(std::move(drawLayer));
224 fNextDrawImageFromLayerID = -1;
225 return;
226 }
227 fRecorder.getRecordingCanvas()->drawImageRect(image, src, dst, sampling, paint, constraint);
228 }
229
230 void onDrawImageLattice2(const SkImage* image,
231 const Lattice& lattice,
232 const SkRect& dst,
233 SkFilterMode mode,
234 const SkPaint* paint) override {
235 fRecorder.getRecordingCanvas()->drawImageLattice(image, lattice, dst, mode, paint);
236 }
237
238 void onDrawAtlas2(const SkImage* image,
239 const SkRSXform rsxForms[],
240 const SkRect src[],
241 const SkColor colors[],
242 int count,
243 SkBlendMode mode,
244 const SkSamplingOptions& sampling,
245 const SkRect* cull,
246 const SkPaint* paint) override {
247 fRecorder.getRecordingCanvas()->drawAtlas(image,
248 rsxForms,
249 src,
250 colors,
251 count,
252 mode,
253 sampling,
254 cull,
255 paint);
256 }
257
258 void onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[],
259 int count,
260 const SkPoint dstClips[],
261 const SkMatrix preViewMatrices[],
262 const SkSamplingOptions& sampling,
263 const SkPaint* paint,
264 SrcRectConstraint constraint) override {
265 fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAImageSet(imageSet,
266 count,
267 dstClips,
268 preViewMatrices,
269 sampling,
270 paint,
271 constraint);
272 }
273
274#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
275 void onDrawEdgeAAQuad(const SkRect& rect,
276 const SkPoint clip[4],
277 SkCanvas::QuadAAFlags aaFlags,
278 const SkColor4f& color,
279 SkBlendMode mode) override {}
280#else
281 void onDrawEdgeAAQuad(const SkRect& rect,
282 const SkPoint clip[4],
283 SkCanvas::QuadAAFlags aaFlags,
284 const SkColor4f& color,
285 SkBlendMode mode) override {
286 fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAQuad(rect,
287 clip,
288 aaFlags,
289 color,
290 mode);
291 }
292#endif
293
294 void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
295 static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
296 static constexpr char kSurfaceID[] = "SurfaceID";
Herb Derbyd7a008392023-03-02 15:36:17 -0500297 TArray<SkString> tokens;
Brian Salomon06c9e292021-04-29 14:10:23 -0400298 SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
299 if (tokens.size() == 2) {
300 if (tokens[0].equals(kOffscreenLayerDraw)) {
301 // Indicates that the next drawPicture command contains the SkPicture to render
302 // to the layer identified by the ID. 'rect' indicates the dirty area to update
303 // (and indicates the layer size if this command is introducing a new layer id).
Brian Salomon33ec61132021-05-12 12:03:02 -0400304 fNextDrawPictureToLayerID = std::stoi(tokens[1].c_str());
305 fNextDrawPictureToLayerClipRect = rect.roundOut();
306 if (fOffscreenLayers->find(fNextDrawPictureToLayerID) == fOffscreenLayers->end()) {
307 SkASSERT(fNextDrawPictureToLayerClipRect.left() == 0 &&
308 fNextDrawPictureToLayerClipRect.top() == 0);
309 (*fOffscreenLayers)[fNextDrawPictureToLayerID].fDimensions =
310 fNextDrawPictureToLayerClipRect.size();
Brian Salomon06c9e292021-04-29 14:10:23 -0400311 }
Brian Salomon33ec61132021-05-12 12:03:02 -0400312 // The next draw picture will notice that fNextDrawPictureToLayerID is set and
313 // redirect the picture to the offscreen layer.
Brian Salomon06c9e292021-04-29 14:10:23 -0400314 return;
315 } else if (tokens[0].equals(kSurfaceID)) {
316 // Indicates that the following drawImageRect should draw an offscreen layer
317 // to this layer.
318 fNextDrawImageFromLayerID = std::stoi(tokens[1].c_str());
319 return;
320 }
321 }
322 }
323
324 void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
325 fRecorder.getRecordingCanvas()->private_draw_shadow_rec(path, rec);
326 }
327
328 void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
329 fRecorder.getRecordingCanvas()->drawDrawable(drawable, matrix);
330 }
331
332 void onDrawPicture(const SkPicture* picture,
333 const SkMatrix* matrix,
334 const SkPaint* paint) override {
Brian Salomon33ec61132021-05-12 12:03:02 -0400335 if (fNextDrawPictureToLayerID != -1) {
Brian Salomon06c9e292021-04-29 14:10:23 -0400336 SkASSERT(!matrix);
337 SkASSERT(!paint);
Brian Osman68556bc2021-07-30 15:24:22 -0400338 LayerCmds* layer = &fOffscreenLayers->at(fNextDrawPictureToLayerID);
Brian Salomon33ec61132021-05-12 12:03:02 -0400339 CmdRecordCanvas sc(layer, fOffscreenLayers, &fNextDrawPictureToLayerClipRect);
Brian Salomon06c9e292021-04-29 14:10:23 -0400340 picture->playback(&sc);
Brian Salomon33ec61132021-05-12 12:03:02 -0400341 fNextDrawPictureToLayerID = -1;
342 fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
Brian Salomon06c9e292021-04-29 14:10:23 -0400343 return;
344 }
345 if (paint) {
346 this->saveLayer(nullptr, paint);
347 }
348 if (matrix) {
349 this->save();
350 this->concat(*matrix);
351 }
352
353 picture->playback(this);
354
355 if (matrix) {
356 this->restore();
357 }
358 if (paint) {
359 this->restore();
360 }
Brian Salomon06c9e292021-04-29 14:10:23 -0400361 }
362
363private:
364 void recordPicCmd() {
365 auto cmd = std::make_unique<PicCmd>();
366 cmd->fContent = fRecorder.finishRecordingAsPicture();
Brian Salomon33ec61132021-05-12 12:03:02 -0400367 cmd->fClipRect = fClipRect;
Brian Salomon06c9e292021-04-29 14:10:23 -0400368 if (cmd->fContent) {
369 fDst->fCmds.push_back(std::move(cmd));
370 }
371 // Set up to accumulate again.
372 fRecorder.beginRecording(SkRect::Make(fDst->fDimensions));
373 }
374
375 SkPictureRecorder fRecorder; // accumulates draws until we draw an offscreen into this layer.
Brian Osman68556bc2021-07-30 15:24:22 -0400376 LayerCmds* fDst = nullptr;
Brian Salomon33ec61132021-05-12 12:03:02 -0400377 SkIRect fClipRect = SkIRect::MakeEmpty();
378 int fNextDrawPictureToLayerID = -1;
379 SkIRect fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
380 int fNextDrawImageFromLayerID = -1;
381 LayerMap* fOffscreenLayers = nullptr;
Brian Salomon06c9e292021-04-29 14:10:23 -0400382};
383
384///////////////////////////////////////////////////////////////////////////////
385
386std::unique_ptr<MSKPPlayer> MSKPPlayer::Make(SkStreamSeekable* stream) {
387 auto deserialContext = std::make_unique<SkSharingDeserialContext>();
388 SkDeserialProcs procs;
389 procs.fImageProc = SkSharingDeserialContext::deserializeImage;
390 procs.fImageCtx = deserialContext.get();
391
Kevin Lubick68de6e32023-10-13 11:19:03 -0400392 int pageCount = SkMultiPictureDocument::ReadPageCount(stream);
Brian Salomon06c9e292021-04-29 14:10:23 -0400393 if (!pageCount) {
394 return nullptr;
395 }
396 std::vector<SkDocumentPage> pages(pageCount);
Kevin Lubick68de6e32023-10-13 11:19:03 -0400397 if (!SkMultiPictureDocument::Read(stream, pages.data(), pageCount, &procs)) {
Brian Salomon06c9e292021-04-29 14:10:23 -0400398 return nullptr;
399 }
400 std::unique_ptr<MSKPPlayer> result(new MSKPPlayer);
401 result->fRootLayers.reserve(pages.size());
402 for (const auto& page : pages) {
403 SkISize dims = {SkScalarCeilToInt(page.fSize.width()),
404 SkScalarCeilToInt(page.fSize.height())};
405 result->fRootLayers.emplace_back();
406 result->fRootLayers.back().fDimensions = dims;
407 result->fMaxDimensions.fWidth = std::max(dims.width() , result->fMaxDimensions.width() );
408 result->fMaxDimensions.fHeight = std::max(dims.height(), result->fMaxDimensions.height());
409 CmdRecordCanvas sc(&result->fRootLayers.back(), &result->fOffscreenLayers);
410 page.fPicture->playback(&sc);
411 }
412 return result;
413}
414
415MSKPPlayer::~MSKPPlayer() = default;
416
417SkISize MSKPPlayer::frameDimensions(int i) const {
418 if (i < 0 || i >= this->numFrames()) {
419 return {-1, -1};
420 }
421 return fRootLayers[i].fDimensions;
422}
423
424bool MSKPPlayer::playFrame(SkCanvas* canvas, int i) {
425 if (i < 0 || i >= this->numFrames()) {
426 return false;
427 }
428
429 // Find the first offscreen layer that has a valid surface. If it's recording context
430 // differs from the passed canvas's then reset all the layers. Playback will
431 // automatically allocate new surfaces for offscreen layers as they're encountered.
432 for (const auto& ols : fOffscreenLayerStates) {
433 const LayerState& state = ols.second;
434 if (state.fSurface) {
435 if (state.fSurface->recordingContext() != canvas->recordingContext()) {
436 this->resetLayers();
437 }
438 break;
439 }
440 }
441
442 // Replay all the commands for this frame to the caller's canvas.
Brian Osman68556bc2021-07-30 15:24:22 -0400443 const LayerCmds& layer = fRootLayers[i];
Brian Salomon06c9e292021-04-29 14:10:23 -0400444 for (const auto& cmd : layer.fCmds) {
445 cmd->draw(canvas, fOffscreenLayers, &fOffscreenLayerStates);
446 }
447 return true;
448}
449
Brian Osman68556bc2021-07-30 15:24:22 -0400450sk_sp<SkSurface> MSKPPlayer::MakeSurfaceForLayer(const LayerCmds& layer, SkCanvas* rootCanvas) {
Brian Salomon31fddc32021-04-30 13:08:55 -0400451 // Assume layer has same surface props and info as this (mskp doesn't currently record this
452 // data).
453 SkSurfaceProps props;
454 rootCanvas->getProps(&props);
455 return rootCanvas->makeSurface(rootCanvas->imageInfo().makeDimensions(layer.fDimensions),
456 &props);
457}
458
Brian Salomon06c9e292021-04-29 14:10:23 -0400459void MSKPPlayer::resetLayers() { fOffscreenLayerStates.clear(); }
Brian Salomon31fddc32021-04-30 13:08:55 -0400460
461void MSKPPlayer::rewindLayers() {
462 for (auto& [id, state] : fOffscreenLayerStates) {
463 state.fCurrCmd = -1;
464 }
465}
466
467void MSKPPlayer::allocateLayers(SkCanvas* canvas) {
468 // Iterate over layers not states as states are lazily created in playback but here we want to
469 // create any that don't already exist.
470 for (auto& [id, layer] : fOffscreenLayers) {
471 LayerState& state = fOffscreenLayerStates[id];
472 if (!state.fSurface || state.fSurface->recordingContext() != canvas->recordingContext()) {
473 state.fCurrCmd = -1;
474 state.fSurface = MakeSurfaceForLayer(fOffscreenLayers[id], canvas);
475 }
476 }
477}
Brian Salomon33ec61132021-05-12 12:03:02 -0400478
479std::vector<int> MSKPPlayer::layerIDs(int frame) const {
480 std::vector<int> result;
481 if (frame < 0) {
482 result.reserve(fOffscreenLayers.size());
483 for (auto& [id, _] : fOffscreenLayers) {
484 result.push_back(id);
485 }
486 return result;
487 }
488 if (frame < static_cast<int>(fRootLayers.size())) {
489 this->collectReferencedLayers(fRootLayers[frame], &result);
490 }
491 return result;
492}
493
494sk_sp<SkImage> MSKPPlayer::layerSnapshot(int layerID) const {
495 auto iter = fOffscreenLayerStates.find(layerID);
496 if (iter == fOffscreenLayerStates.end() || !iter->second.fSurface) {
497 return nullptr;
498 }
499 return iter->second.fSurface->makeImageSnapshot();
500}
501
Brian Osman68556bc2021-07-30 15:24:22 -0400502void MSKPPlayer::collectReferencedLayers(const LayerCmds& layer, std::vector<int>* out) const {
Brian Salomon33ec61132021-05-12 12:03:02 -0400503 for (const auto& cmd : layer.fCmds) {
504 if (int id = cmd->layerID(); id >= 0) {
505 // Linear, but we'd need to have a lot of layers to actually care.
506 if (std::find(out->begin(), out->end(), id) == out->end()) {
507 out->push_back(id);
508 auto iter = fOffscreenLayers.find(id);
509 SkASSERT(iter != fOffscreenLayers.end());
510 this->collectReferencedLayers(iter->second, out);
511 }
512 }
513 }
514}