blob: e7014f6b8f1dd0f6461f1dc7f208957c27fd7b38 [file] [log] [blame]
/*
* Copyright 2021 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef MSKPPlayer_DEFINED
#define MSKPPlayer_DEFINED
#include "include/core/SkRefCnt.h"
#include "include/core/SkSize.h"
#include <unordered_map>
#include <vector>
class SkCanvas;
class SkImage;
class SkStreamSeekable;
class SkSurface;
/**
* Plays frames/pages of a MSKP to a canvas. This class uses the term "frame" as though the MSKP
* contains an animation, though it could indeed contain pages of a static document.
*/
class MSKPPlayer {
public:
~MSKPPlayer();
/** Make a player from a MSKP stream, or null if stream can't be read as MSKP. */
static std::unique_ptr<MSKPPlayer> Make(SkStreamSeekable* stream);
/** Maximum width and height across all frames. */
SkISize maxDimensions() const { return fMaxDimensions; }
/** Total number of frames. */
int numFrames() const { return static_cast<int>(fRootLayers.size()); }
/** Size of an individual frame. */
SkISize frameDimensions(int i) const;
/**
* Plays a frame into the passed canvas. Frames can be randomly accessed. Offscreen layers are
* incrementally updated from their current state to the state required for the frame
* (redrawing from scratch if their current state is ahead of the passed frame index).
*/
bool playFrame(SkCanvas* canvas, int i);
/** Destroys any cached offscreen layers. */
void resetLayers();
/**
* Forces all offscreen layers to re-render the next time they're required for a frame but
* preserves the backing stores for them if already allocated.
*/
void rewindLayers();
/**
* Creates backing stores for any offscreen layers using the passed canvas's makeSurface().
* Existing layers that match the canvas's recording context are not reallocated or rewound.
*/
void allocateLayers(SkCanvas*);
/**
* A set of IDs of offscreen layers in no particular order. If frame value >= 0 is specified
* then the layer set is filtered to layers used by that frame (or empty if >= numFrames). If
* < 0 then gathers all the layers across all frames.
*/
std::vector<int> layerIDs(int frame = -1) const;
/**
* Gets the contents of an offscreen layer. It's contents will depend on current playback state
* (playFrame(), updateFrameLayers(), resetLayers()). If the layer currently has no backing
* store because it hasn't been drawn or resetLayers() was called then this will return nullptr.
* Layer contents are not affected by rewindLayers() as that simply lazily redraws the frame
* contents the next time it is required by playFrame*() or updateFrameLayers().
*/
sk_sp<SkImage> layerSnapshot(int layerID) const;
private:
MSKPPlayer() = default;
// noncopyable, nonmoveable.
MSKPPlayer(const MSKPPlayer&) = delete;
MSKPPlayer(MSKPPlayer&&) = delete;
MSKPPlayer& operator=(const MSKPPlayer&) = delete;
MSKPPlayer& operator=(MSKPPlayer&&) = delete;
// Cmds are used to draw content to the frame root layer and to offscreen layers.
struct Cmd;
// Draws a SkPicture.
struct PicCmd;
// Draws another layer. Stores the ID of the layer to draw and what command index on that
// layer should be current when the layer is drawn. The layer contents are updated to the
// stored command index before the layer is drawn.
struct DrawLayerCmd;
// The commands for a root/offscreen layer and dimensions of the layer.
struct LayerCmds {
LayerCmds() = default;
LayerCmds(LayerCmds&&) = default;
SkISize fDimensions;
std::vector<std::unique_ptr<Cmd>> fCmds;
};
// Playback state of layer: the last command index drawn to it and the SkSurface with contents.
struct LayerState {
size_t fCurrCmd = -1;
sk_sp<SkSurface> fSurface;
};
static sk_sp<SkSurface> MakeSurfaceForLayer(const LayerCmds&, SkCanvas* rootCanvas);
void collectReferencedLayers(const LayerCmds& layer, std::vector<int>*) const;
// MSKP layer ID -> LayerCmds
using LayerMap = std::unordered_map<int, LayerCmds>;
// MSKP layer ID -> LayerState
using LayerStateMap = std::unordered_map<int, LayerState>;
/**
* A SkCanvas that consumes the SkPicture and records Cmds into a Layer. It will spawn
* additional Layers and record nested SkPictures into those using additional CmdRecordCanvas
* CmdRecordCanvas instances. It needs access to fOffscreenLayers to create and update LayerCmds
* structs for offscreen layers.
*/
class CmdRecordCanvas;
SkISize fMaxDimensions = {0, 0}; // Max dimensions across all frames.
LayerMap fOffscreenLayers; // All the offscreen layers for all frames.
LayerStateMap fOffscreenLayerStates; // Current surfaces and command idx for offscreen
// layers
std::vector<LayerCmds> fRootLayers; // One root layer for each frame.
};
#endif