Coverage Report

Created: 2024-05-20 07:14

/src/skia/tools/MSKPPlayer.cpp
Line
Count
Source (jump to first uncovered line)
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/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
0
    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.
34
0
    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
42
0
    bool isFullRedraw(SkCanvas* canvas) const override {
43
0
        if (fClipRect.isEmpty()) {
44
0
            return false;
45
0
        }
46
0
        return fClipRect.contains(SkIRect::MakeSize(canvas->getBaseLayerSize()));
47
0
    }
48
49
0
    void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override {
50
0
        if (!fClipRect.isEmpty()) {
51
0
            canvas->save();
52
0
            canvas->clipIRect(fClipRect);
53
0
        }
54
0
        canvas->drawPicture(fContent.get());
55
0
        if (!fClipRect.isEmpty()) {
56
0
            canvas->restore();
57
0
        }
58
0
    }
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
73
0
    bool isFullRedraw(SkCanvas* canvas) const override { return false; }
74
    void draw(SkCanvas* canvas, const LayerMap&, LayerStateMap*) const override;
75
0
    int layerID() const override { return fLayerId; }
76
};
77
78
void MSKPPlayer::DrawLayerCmd::draw(SkCanvas* canvas,
79
                                    const LayerMap& layerMap,
80
0
                                    LayerStateMap* layerStateMap) const {
81
0
    const LayerCmds& layer = layerMap.at(fLayerId);
82
0
    LayerState* layerState = &(*layerStateMap)[fLayerId];
83
0
    if (!layerState->fSurface) {
84
0
        layerState->fCurrCmd = 0;
85
0
        layerState->fSurface = MSKPPlayer::MakeSurfaceForLayer(layer, canvas);
86
0
        if (!layerState->fSurface) {
87
0
            SkDebugf("Couldn't create surface for layer");
88
0
            return;
89
0
        }
90
0
    }
91
0
    size_t cmd = layerState->fCurrCmd;
92
0
    if (cmd > fLayerCmdCnt) {
93
        // If the layer contains contents from later commands then replay from the beginning.
94
0
        cmd = 0;
95
0
    }
96
0
    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
0
    for (size_t checkCmd = fLayerCmdCnt - 1; checkCmd > cmd; --checkCmd) {
100
0
        if (layer.fCmds[checkCmd]->isFullRedraw(layerCanvas)) {
101
0
            cmd = checkCmd;
102
0
            break;
103
0
        }
104
0
    }
105
0
    for (; cmd < fLayerCmdCnt; ++cmd) {
106
0
        if (cmd == 0 || layer.fCmds[cmd]->isFullRedraw(layerCanvas)) {
107
0
            layerState->fSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
108
0
        }
109
0
        layer.fCmds[cmd]->draw(layerCanvas, layerMap, layerStateMap);
110
0
    }
111
0
    layerState->fCurrCmd = fLayerCmdCnt;
112
0
    const SkPaint* paint = fPaint.isValid() ? fPaint.get() : nullptr;
113
0
    canvas->drawImageRect(layerState->fSurface->makeImageSnapshot(),
114
0
                          fSrcRect,
115
0
                          fDstRect,
116
0
                          fSampling,
117
0
                          paint,
118
0
                          fConstraint);
119
0
}
120
121
///////////////////////////////////////////////////////////////////////////////
122
123
class MSKPPlayer::CmdRecordCanvas : public SkCanvasVirtualEnforcer<SkCanvas> {
124
public:
125
    CmdRecordCanvas(LayerCmds* dst, LayerMap* offscreenLayers, const SkIRect* clipRect = nullptr)
126
0
            : fDst(dst), fOffscreenLayers(offscreenLayers) {
127
0
        if (clipRect) {
128
0
            fClipRect = *clipRect;
129
0
        }
130
0
        fRecorder.beginRecording(SkRect::Make(dst->fDimensions));
131
0
    }
132
0
    ~CmdRecordCanvas() override { this->recordPicCmd(); }
133
134
protected:
135
0
    void onDrawPaint(const SkPaint& paint) override {
136
0
        fRecorder.getRecordingCanvas()->drawPaint(paint);
137
0
    }
138
139
0
    void onDrawBehind(const SkPaint& paint) override {
140
0
        SkCanvasPriv::DrawBehind(fRecorder.getRecordingCanvas(), paint);
141
0
    }
142
143
0
    void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
144
0
        fRecorder.getRecordingCanvas()->drawRect(rect, paint);
145
0
    }
146
147
0
    void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
148
0
        fRecorder.getRecordingCanvas()->drawRRect(rrect, paint);
149
0
    }
150
151
0
    void onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) override {
152
0
        fRecorder.getRecordingCanvas()->drawDRRect(outer, inner, paint);
153
0
    }
154
155
0
    void onDrawOval(const SkRect& rect, const SkPaint& paint) override {
156
0
        fRecorder.getRecordingCanvas()->drawOval(rect, paint);
157
0
    }
158
159
    void onDrawArc(const SkRect& rect,
160
                   SkScalar startAngle,
161
                   SkScalar sweepAngle,
162
                   bool useCenter,
163
0
                   const SkPaint& paint) override {
164
0
        fRecorder.getRecordingCanvas()->drawArc(rect, startAngle, sweepAngle, useCenter, paint);
165
0
    }
166
167
0
    void onDrawPath(const SkPath& path, const SkPaint& paint) override {
168
0
        fRecorder.getRecordingCanvas()->drawPath(path, paint);
169
0
    }
170
171
0
    void onDrawRegion(const SkRegion& region, const SkPaint& paint) override {
172
0
        fRecorder.getRecordingCanvas()->drawRegion(region, paint);
173
0
    }
174
175
    void onDrawTextBlob(const SkTextBlob* blob,
176
                        SkScalar x,
177
                        SkScalar y,
178
0
                        const SkPaint& paint) override {
179
0
        fRecorder.getRecordingCanvas()->drawTextBlob(blob, x, y, paint);
180
0
    }
181
182
    void onDrawPatch(const SkPoint cubics[12],
183
                     const SkColor colors[4],
184
                     const SkPoint texCoords[4],
185
                     SkBlendMode mode,
186
0
                     const SkPaint& paint) override {
187
0
        fRecorder.getRecordingCanvas()->drawPatch(cubics, colors, texCoords, mode, paint);
188
0
    }
189
190
    void onDrawPoints(SkCanvas::PointMode mode,
191
                      size_t count,
192
                      const SkPoint pts[],
193
0
                      const SkPaint& paint) override {
194
0
        fRecorder.getRecordingCanvas()->drawPoints(mode, count, pts, paint);
195
0
    }
196
197
    void onDrawImage2(const SkImage* image,
198
                      SkScalar dx,
199
                      SkScalar dy,
200
                      const SkSamplingOptions& sampling,
201
0
                      const SkPaint* paint) override {
202
0
        fRecorder.getRecordingCanvas()->drawImage(image, dx, dy, sampling, paint);
203
0
    }
204
205
    void onDrawImageRect2(const SkImage* image,
206
                          const SkRect& src,
207
                          const SkRect& dst,
208
                          const SkSamplingOptions& sampling,
209
                          const SkPaint* paint,
210
0
                          SrcRectConstraint constraint) override {
211
0
        if (fNextDrawImageFromLayerID != -1) {
212
0
            this->recordPicCmd();
213
0
            auto drawLayer = std::make_unique<DrawLayerCmd>();
214
0
            drawLayer->fLayerId = fNextDrawImageFromLayerID;
215
0
            drawLayer->fLayerCmdCnt = fOffscreenLayers->at(fNextDrawImageFromLayerID).fCmds.size();
216
0
            drawLayer->fSrcRect = src;
217
0
            drawLayer->fDstRect = dst;
218
0
            drawLayer->fSampling = sampling;
219
0
            drawLayer->fConstraint = constraint;
220
0
            if (paint) {
221
0
                drawLayer->fPaint.init(*paint);
222
0
            }
223
0
            fDst->fCmds.push_back(std::move(drawLayer));
224
0
            fNextDrawImageFromLayerID = -1;
225
0
            return;
226
0
        }
227
0
        fRecorder.getRecordingCanvas()->drawImageRect(image, src, dst, sampling, paint, constraint);
228
0
    }
229
230
    void onDrawImageLattice2(const SkImage* image,
231
                             const Lattice& lattice,
232
                             const SkRect& dst,
233
                             SkFilterMode mode,
234
0
                             const SkPaint* paint) override {
235
0
        fRecorder.getRecordingCanvas()->drawImageLattice(image, lattice, dst, mode, paint);
236
0
    }
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
0
                      const SkPaint* paint) override {
247
0
        fRecorder.getRecordingCanvas()->drawAtlas(image,
248
0
                                                  rsxForms,
249
0
                                                  src,
250
0
                                                  colors,
251
0
                                                  count,
252
0
                                                  mode,
253
0
                                                  sampling,
254
0
                                                  cull,
255
0
                                                  paint);
256
0
    }
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
0
                               SrcRectConstraint constraint) override {
265
0
        fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAImageSet(imageSet,
266
0
                                                                        count,
267
0
                                                                        dstClips,
268
0
                                                                        preViewMatrices,
269
0
                                                                        sampling,
270
0
                                                                        paint,
271
0
                                                                        constraint);
272
0
    }
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
0
                          SkBlendMode mode) override {
286
0
        fRecorder.getRecordingCanvas()->experimental_DrawEdgeAAQuad(rect,
287
0
                                                                    clip,
288
0
                                                                    aaFlags,
289
0
                                                                    color,
290
0
                                                                    mode);
291
0
    }
292
#endif
293
294
0
    void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override {
295
0
        static constexpr char kOffscreenLayerDraw[] = "OffscreenLayerDraw";
296
0
        static constexpr char kSurfaceID[] = "SurfaceID";
297
0
        TArray<SkString> tokens;
298
0
        SkStrSplit(key, "|", kStrict_SkStrSplitMode, &tokens);
299
0
        if (tokens.size() == 2) {
300
0
            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
0
                fNextDrawPictureToLayerID = std::stoi(tokens[1].c_str());
305
0
                fNextDrawPictureToLayerClipRect = rect.roundOut();
306
0
                if (fOffscreenLayers->find(fNextDrawPictureToLayerID) == fOffscreenLayers->end()) {
307
0
                    SkASSERT(fNextDrawPictureToLayerClipRect.left() == 0 &&
308
0
                             fNextDrawPictureToLayerClipRect.top()  == 0);
309
0
                    (*fOffscreenLayers)[fNextDrawPictureToLayerID].fDimensions =
310
0
                            fNextDrawPictureToLayerClipRect.size();
311
0
                }
312
                // The next draw picture will notice that fNextDrawPictureToLayerID is set and
313
                // redirect the picture to the offscreen layer.
314
0
                return;
315
0
            } else if (tokens[0].equals(kSurfaceID)) {
316
                // Indicates that the following drawImageRect should draw an offscreen layer
317
                // to this layer.
318
0
                fNextDrawImageFromLayerID = std::stoi(tokens[1].c_str());
319
0
                return;
320
0
            }
321
0
        }
322
0
    }
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawAnnotation(SkRect const&, char const*, SkData*)
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawAnnotation(SkRect const&, char const*, SkData*)
323
324
0
    void onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) override {
325
0
        fRecorder.getRecordingCanvas()->private_draw_shadow_rec(path, rec);
326
0
    }
327
328
0
    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
329
0
        fRecorder.getRecordingCanvas()->drawDrawable(drawable, matrix);
330
0
    }
331
332
    void onDrawPicture(const SkPicture* picture,
333
                       const SkMatrix* matrix,
334
0
                       const SkPaint* paint) override {
335
0
        if (fNextDrawPictureToLayerID != -1) {
336
0
            SkASSERT(!matrix);
337
0
            SkASSERT(!paint);
338
0
            LayerCmds* layer = &fOffscreenLayers->at(fNextDrawPictureToLayerID);
339
0
            CmdRecordCanvas sc(layer, fOffscreenLayers, &fNextDrawPictureToLayerClipRect);
340
0
            picture->playback(&sc);
341
0
            fNextDrawPictureToLayerID = -1;
342
0
            fNextDrawPictureToLayerClipRect = SkIRect::MakeEmpty();
343
0
            return;
344
0
        }
345
0
        if (paint) {
346
0
            this->saveLayer(nullptr, paint);
347
0
        }
348
0
        if (matrix) {
349
0
            this->save();
350
0
            this->concat(*matrix);
351
0
        }
352
353
0
        picture->playback(this);
354
355
0
        if (matrix) {
356
0
            this->restore();
357
0
        }
358
0
        if (paint) {
359
0
            this->restore();
360
0
        }
361
0
    }
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawPicture(SkPicture const*, SkMatrix const*, SkPaint const*)
Unexecuted instantiation: MSKPPlayer::CmdRecordCanvas::onDrawPicture(SkPicture const*, SkMatrix const*, SkPaint const*)
362
363
private:
364
0
    void recordPicCmd() {
365
0
        auto cmd = std::make_unique<PicCmd>();
366
0
        cmd->fContent = fRecorder.finishRecordingAsPicture();
367
0
        cmd->fClipRect = fClipRect;
368
0
        if (cmd->fContent) {
369
0
            fDst->fCmds.push_back(std::move(cmd));
370
0
        }
371
        // Set up to accumulate again.
372
0
        fRecorder.beginRecording(SkRect::Make(fDst->fDimensions));
373
0
    }
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
386
0
std::unique_ptr<MSKPPlayer> MSKPPlayer::Make(SkStreamSeekable* stream) {
387
0
    auto deserialContext = std::make_unique<SkSharingDeserialContext>();
388
0
    SkDeserialProcs procs;
389
0
    procs.fImageProc = SkSharingDeserialContext::deserializeImage;
390
0
    procs.fImageCtx = deserialContext.get();
391
392
0
    int pageCount = SkMultiPictureDocument::ReadPageCount(stream);
393
0
    if (!pageCount) {
394
0
        return nullptr;
395
0
    }
396
0
    std::vector<SkDocumentPage> pages(pageCount);
397
0
    if (!SkMultiPictureDocument::Read(stream, pages.data(), pageCount, &procs)) {
398
0
        return nullptr;
399
0
    }
400
0
    std::unique_ptr<MSKPPlayer> result(new MSKPPlayer);
401
0
    result->fRootLayers.reserve(pages.size());
402
0
    for (const auto& page : pages) {
403
0
        SkISize dims = {SkScalarCeilToInt(page.fSize.width()),
404
0
                        SkScalarCeilToInt(page.fSize.height())};
405
0
        result->fRootLayers.emplace_back();
406
0
        result->fRootLayers.back().fDimensions = dims;
407
0
        result->fMaxDimensions.fWidth  = std::max(dims.width() , result->fMaxDimensions.width() );
408
0
        result->fMaxDimensions.fHeight = std::max(dims.height(), result->fMaxDimensions.height());
409
0
        CmdRecordCanvas sc(&result->fRootLayers.back(), &result->fOffscreenLayers);
410
0
        page.fPicture->playback(&sc);
411
0
    }
412
0
    return result;
413
0
}
414
415
0
MSKPPlayer::~MSKPPlayer() = default;
416
417
0
SkISize MSKPPlayer::frameDimensions(int i) const {
418
0
    if (i < 0 || i >= this->numFrames()) {
419
0
        return {-1, -1};
420
0
    }
421
0
    return fRootLayers[i].fDimensions;
422
0
}
423
424
0
bool MSKPPlayer::playFrame(SkCanvas* canvas, int i) {
425
0
    if (i < 0 || i >= this->numFrames()) {
426
0
        return false;
427
0
    }
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
0
    for (const auto& ols : fOffscreenLayerStates) {
433
0
        const LayerState& state = ols.second;
434
0
        if (state.fSurface) {
435
0
            if (state.fSurface->recordingContext() != canvas->recordingContext()) {
436
0
                this->resetLayers();
437
0
            }
438
0
            break;
439
0
        }
440
0
    }
441
442
    // Replay all the commands for this frame to the caller's canvas.
443
0
    const LayerCmds& layer = fRootLayers[i];
444
0
    for (const auto& cmd : layer.fCmds) {
445
0
        cmd->draw(canvas, fOffscreenLayers, &fOffscreenLayerStates);
446
0
    }
447
0
    return true;
448
0
}
449
450
0
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
0
    SkSurfaceProps props;
454
0
    rootCanvas->getProps(&props);
455
0
    return rootCanvas->makeSurface(rootCanvas->imageInfo().makeDimensions(layer.fDimensions),
456
0
                                   &props);
457
0
}
458
459
0
void MSKPPlayer::resetLayers() { fOffscreenLayerStates.clear(); }
460
461
0
void MSKPPlayer::rewindLayers() {
462
0
    for (auto& [id, state] : fOffscreenLayerStates) {
463
0
        state.fCurrCmd = -1;
464
0
    }
465
0
}
466
467
0
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
0
    for (auto& [id, layer] : fOffscreenLayers) {
471
0
        LayerState& state = fOffscreenLayerStates[id];
472
0
        if (!state.fSurface || state.fSurface->recordingContext() != canvas->recordingContext()) {
473
0
            state.fCurrCmd = -1;
474
0
            state.fSurface = MakeSurfaceForLayer(fOffscreenLayers[id], canvas);
475
0
        }
476
0
    }
477
0
}
478
479
0
std::vector<int> MSKPPlayer::layerIDs(int frame) const {
480
0
    std::vector<int> result;
481
0
    if (frame < 0) {
482
0
        result.reserve(fOffscreenLayers.size());
483
0
        for (auto& [id, _] : fOffscreenLayers) {
484
0
            result.push_back(id);
485
0
        }
486
0
        return result;
487
0
    }
488
0
    if (frame < static_cast<int>(fRootLayers.size())) {
489
0
        this->collectReferencedLayers(fRootLayers[frame], &result);
490
0
    }
491
0
    return result;
492
0
}
493
494
0
sk_sp<SkImage> MSKPPlayer::layerSnapshot(int layerID) const {
495
0
    auto iter = fOffscreenLayerStates.find(layerID);
496
0
    if (iter == fOffscreenLayerStates.end() || !iter->second.fSurface) {
497
0
        return nullptr;
498
0
    }
499
0
    return iter->second.fSurface->makeImageSnapshot();
500
0
}
501
502
0
void MSKPPlayer::collectReferencedLayers(const LayerCmds& layer, std::vector<int>* out) const {
503
0
    for (const auto& cmd : layer.fCmds) {
504
0
        if (int id = cmd->layerID(); id >= 0) {
505
            // Linear, but we'd need to have a lot of layers to actually care.
506
0
            if (std::find(out->begin(), out->end(), id) == out->end()) {
507
0
                out->push_back(id);
508
0
                auto iter = fOffscreenLayers.find(id);
509
0
                SkASSERT(iter != fOffscreenLayers.end());
510
0
                this->collectReferencedLayers(iter->second, out);
511
0
            }
512
0
        }
513
0
    }
514
0
}
Unexecuted instantiation: MSKPPlayer::collectReferencedLayers(MSKPPlayer::LayerCmds const&, std::__1::vector<int, std::__1::allocator<int> >*) const
Unexecuted instantiation: MSKPPlayer::collectReferencedLayers(MSKPPlayer::LayerCmds const&, std::__1::vector<int, std::__1::allocator<int> >*) const