xref: /aosp_15_r20/external/skia/tools/MSKPPlayer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
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"
15 #include "include/docs/SkMultiPictureDocument.h"
16 #include "include/gpu/ganesh/GrDirectContext.h"
17 #include "include/private/base/SkTArray.h"
18 #include "include/utils/SkNoDrawCanvas.h"
19 #include "src/base/SkTLazy.h"
20 #include "src/core/SkCanvasPriv.h"
21 #include "src/core/SkStringUtils.h"
22 #include "tools/SkSharingProc.h"
23 
24 using namespace skia_private;
25 
26 ///////////////////////////////////////////////////////////////////////////////
27 
28 // Base Cmd struct.
29 struct MSKPPlayer::Cmd {
30     virtual ~Cmd() = default;
31     virtual bool isFullRedraw(SkCanvas*) const = 0;
32     virtual void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const = 0;
33     // If this command draws another layer, return it's ID. Otherwise, -1.
layerIDMSKPPlayer::Cmd34     virtual int layerID() const { return -1; }
35 };
36 
37 // Draws a SkPicture.
38 struct MSKPPlayer::PicCmd : Cmd {
39     sk_sp<SkPicture> fContent;
40     SkIRect fClipRect = SkIRect::MakeEmpty(); // clip for picture (no clip if empty).
41 
isFullRedrawMSKPPlayer::PicCmd42     bool isFullRedraw(SkCanvas* canvas) const override {
43         if (fClipRect.isEmpty()) {
44             return false;
45         }
46         return fClipRect.contains(SkIRect::MakeSize(canvas->getBaseLayerSize()));
47     }
48 
drawMSKPPlayer::PicCmd49     void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override {
50         if (!fClipRect.isEmpty()) {
51             canvas->save();
52             canvas->clipIRect(fClipRect);
53         }
54         canvas->drawPicture(fContent.get());
55         if (!fClipRect.isEmpty()) {
56             canvas->restore();
57         }
58     }
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.
64 struct 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 
isFullRedrawMSKPPlayer::DrawLayerCmd73     bool isFullRedraw(SkCanvas* canvas) const override { return false; }
74     void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override;
layerIDMSKPPlayer::DrawLayerCmd75     int layerID() const override { return fLayerId; }
76 };
77 
draw(SkCanvas * canvas,const LayerMap & layerMap,LayerStateMap * layerStateMap) const78 void MSKPPlayer::DrawLayerCmd::draw(SkCanvas* canvas,
79                                     const LayerMap& layerMap,
80                                     LayerStateMap* layerStateMap) const {
81     const LayerCmds& layer = layerMap.at(fLayerId);
82     LayerState* layerState = &(*layerStateMap)[fLayerId];
83     if (!layerState->fSurface) {
84         layerState->fCurrCmd = 0;
85         layerState->fSurface = MSKPPlayer::MakeSurfaceForLayer(layer, canvas);
86         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();
97     // 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     }
105     for (; cmd < fLayerCmdCnt; ++cmd) {
106         if (cmd == 0 || layer.fCmds[cmd]->isFullRedraw(layerCanvas)) {
107             layerState->fSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
108         }
109         layer.fCmds[cmd]->draw(layerCanvas, layerMap, layerStateMap);
110     }
111     layerState->fCurrCmd = fLayerCmdCnt;
112     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 
123 class MSKPPlayer::CmdRecordCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
124 public:
CmdRecordCanvas(LayerCmds * dst,LayerMap * offscreenLayers,const SkIRect * clipRect=nullptr)125     CmdRecordCanvas(LayerCmds* dst, LayerMap* offscreenLayers, const SkIRect* clipRect = nullptr)
126             : fDst(dst), fOffscreenLayers(offscreenLayers) {
127         if (clipRect) {
128             fClipRect = *clipRect;
129         }
130         fRecorder.beginRecording(SkRect::Make(dst->fDimensions));
131     }
~CmdRecordCanvas()132     ~CmdRecordCanvas() override { this->recordPicCmd(); }
133 
134 protected:
onDrawPaint(const SkPaint & paint)135     void onDrawPaint(const SkPaint& paint) override {
136         fRecorder.getRecordingCanvas()->drawPaint(paint);
137     }
138 
onDrawBehind(const SkPaint & paint)139     void onDrawBehind(const SkPaint& paint) override {
140         SkCanvasPriv::DrawBehind(fRecorder.getRecordingCanvas(), paint);
141     }
142 
onDrawRect(const SkRect & rect,const SkPaint & paint)143     void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
144         fRecorder.getRecordingCanvas()->drawRect(rect, paint);
145     }
146 
onDrawRRect(const SkRRect & rrect,const SkPaint & paint)147     void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
148         fRecorder.getRecordingCanvas()->drawRRect(rrect, paint);
149     }
150 
onDrawDRRect(const SkRRect & outer,const SkRRect & inner,const SkPaint & paint)151     void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
152         fRecorder.getRecordingCanvas()->drawDRRect(outer, inner, paint);
153     }
154 
onDrawOval(const SkRect & rect,const SkPaint & paint)155     void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
156         fRecorder.getRecordingCanvas()->drawOval(rect, paint);
157     }
158 
onDrawArc(const SkRect & rect,SkScalar startAngle,SkScalar sweepAngle,bool useCenter,const SkPaint & paint)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 
onDrawPath(const SkPath & path,const SkPaint & paint)167     void onDrawPath(const SkPath& path, const SkPaint& paint) override {
168         fRecorder.getRecordingCanvas()->drawPath(path, paint);
169     }
170 
onDrawRegion(const SkRegion & region,const SkPaint & paint)171     void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
172         fRecorder.getRecordingCanvas()->drawRegion(region, paint);
173     }
174 
onDrawTextBlob(const SkTextBlob * blob,SkScalar x,SkScalar y,const SkPaint & paint)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 
onDrawPatch(const SkPoint cubics[12],const SkColor colors[4],const SkPoint texCoords[4],SkBlendMode mode,const SkPaint & paint)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 
onDrawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint pts[],const SkPaint & paint)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 
onDrawImage2(const SkImage * image,SkScalar dx,SkScalar dy,const SkSamplingOptions & sampling,const SkPaint * paint)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 
onDrawImageRect2(const SkImage * image,const SkRect & src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)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 
onDrawImageLattice2(const SkImage * image,const Lattice & lattice,const SkRect & dst,SkFilterMode mode,const SkPaint * paint)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 
onDrawAtlas2(const SkImage * image,const SkRSXform rsxForms[],const SkRect src[],const SkColor colors[],int count,SkBlendMode mode,const SkSamplingOptions & sampling,const SkRect * cull,const SkPaint * paint)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 
onDrawEdgeAAImageSet2(const ImageSetEntry imageSet[],int count,const SkPoint dstClips[],const SkMatrix preViewMatrices[],const SkSamplingOptions & sampling,const SkPaint * paint,SrcRectConstraint constraint)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
onDrawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],SkCanvas::QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)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
onDrawEdgeAAQuad(const SkRect & rect,const SkPoint clip[4],SkCanvas::QuadAAFlags aaFlags,const SkColor4f & color,SkBlendMode mode)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 
onDrawAnnotation(const SkRect & rect,const char key[],SkData * value)294     void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
295         static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
296         static constexpr char kSurfaceID[] = "SurfaceID";
297         TArray<SkString> tokens;
298         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).
304                 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();
311                 }
312                 // The next draw picture will notice that fNextDrawPictureToLayerID is set and
313                 // redirect the picture to the offscreen layer.
314                 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 
onDrawShadowRec(const SkPath & path,const SkDrawShadowRec & rec)324     void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
325         fRecorder.getRecordingCanvas()->private_draw_shadow_rec(path, rec);
326     }
327 
onDrawDrawable(SkDrawable * drawable,const SkMatrix * matrix)328     void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
329         fRecorder.getRecordingCanvas()->drawDrawable(drawable, matrix);
330     }
331 
onDrawPicture(const SkPicture * picture,const SkMatrix * matrix,const SkPaint * paint)332     void onDrawPicture(const SkPicture* picture,
333                        const SkMatrix* matrix,
334                        const SkPaint* paint) override {
335         if (fNextDrawPictureToLayerID != -1) {
336             SkASSERT(!matrix);
337             SkASSERT(!paint);
338             LayerCmds* layer = &fOffscreenLayers->at(fNextDrawPictureToLayerID);
339             CmdRecordCanvas sc(layer, fOffscreenLayers, &fNextDrawPictureToLayerClipRect);
340             picture->playback(&sc);
341             fNextDrawPictureToLayerID = -1;
342             fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
343             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         }
361     }
362 
363 private:
recordPicCmd()364     void recordPicCmd() {
365         auto cmd = std::make_unique<PicCmd>();
366         cmd->fContent = fRecorder.finishRecordingAsPicture();
367         cmd->fClipRect = fClipRect;
368         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.
376     LayerCmds*        fDst                            = nullptr;
377     SkIRect           fClipRect                       = SkIRect::MakeEmpty();
378     int               fNextDrawPictureToLayerID       = -1;
379     SkIRect           fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
380     int               fNextDrawImageFromLayerID       = -1;
381     LayerMap*         fOffscreenLayers                = nullptr;
382 };
383 
384 ///////////////////////////////////////////////////////////////////////////////
385 
Make(SkStreamSeekable * stream)386 std::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 
392     int pageCount = SkMultiPictureDocument::ReadPageCount(stream);
393     if (!pageCount) {
394         return nullptr;
395     }
396     std::vector<SkDocumentPage> pages(pageCount);
397     if (!SkMultiPictureDocument::Read(stream, pages.data(), pageCount, &procs)) {
398         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 
415 MSKPPlayer::~MSKPPlayer() = default;
416 
frameDimensions(int i) const417 SkISize MSKPPlayer::frameDimensions(int i) const {
418     if (i < 0 || i >= this->numFrames()) {
419         return {-1, -1};
420     }
421     return fRootLayers[i].fDimensions;
422 }
423 
playFrame(SkCanvas * canvas,int i)424 bool 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.
443     const LayerCmds& layer = fRootLayers[i];
444     for (const auto& cmd : layer.fCmds) {
445         cmd->draw(canvas, fOffscreenLayers, &fOffscreenLayerStates);
446     }
447     return true;
448 }
449 
MakeSurfaceForLayer(const LayerCmds & layer,SkCanvas * rootCanvas)450 sk_sp<SkSurface> MSKPPlayer::MakeSurfaceForLayer(const LayerCmds& layer, SkCanvas* rootCanvas) {
451     // 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 
resetLayers()459 void MSKPPlayer::resetLayers() { fOffscreenLayerStates.clear(); }
460 
rewindLayers()461 void MSKPPlayer::rewindLayers() {
462     for (auto& [id, state] : fOffscreenLayerStates) {
463         state.fCurrCmd = -1;
464     }
465 }
466 
allocateLayers(SkCanvas * canvas)467 void 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 }
478 
layerIDs(int frame) const479 std::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 
layerSnapshot(int layerID) const494 sk_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 
collectReferencedLayers(const LayerCmds & layer,std::vector<int> * out) const502 void MSKPPlayer::collectReferencedLayers(const LayerCmds& layer, std::vector<int>* out) const {
503     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 }
515