Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 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 | #ifndef MSKPPlayer_DEFINED |
| 9 | #define MSKPPlayer_DEFINED |
| 10 | |
| 11 | #include "include/core/SkRefCnt.h" |
| 12 | #include "include/core/SkSize.h" |
| 13 | |
| 14 | #include <unordered_map> |
| 15 | #include <vector> |
| 16 | |
| 17 | class SkCanvas; |
Brian Salomon | 33ec6113 | 2021-05-12 12:03:02 -0400 | [diff] [blame] | 18 | class SkImage; |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 19 | class SkStreamSeekable; |
| 20 | class SkSurface; |
| 21 | |
| 22 | /** |
| 23 | * Plays frames/pages of a MSKP to a canvas. This class uses the term "frame" as though the MSKP |
| 24 | * contains an animation, though it could indeed contain pages of a static document. |
| 25 | */ |
| 26 | class MSKPPlayer { |
| 27 | public: |
| 28 | ~MSKPPlayer(); |
| 29 | |
| 30 | /** Make a player from a MSKP stream, or null if stream can't be read as MSKP. */ |
| 31 | static std::unique_ptr<MSKPPlayer> Make(SkStreamSeekable* stream); |
| 32 | |
| 33 | /** Maximum width and height across all frames. */ |
| 34 | SkISize maxDimensions() const { return fMaxDimensions; } |
| 35 | |
| 36 | /** Total number of frames. */ |
| 37 | int numFrames() const { return static_cast<int>(fRootLayers.size()); } |
| 38 | |
| 39 | /** Size of an individual frame. */ |
| 40 | SkISize frameDimensions(int i) const; |
| 41 | |
| 42 | /** |
| 43 | * Plays a frame into the passed canvas. Frames can be randomly accessed. Offscreen layers are |
| 44 | * incrementally updated from their current state to the state required for the frame |
| 45 | * (redrawing from scratch if their current state is ahead of the passed frame index). |
| 46 | */ |
| 47 | bool playFrame(SkCanvas* canvas, int i); |
| 48 | |
| 49 | /** Destroys any cached offscreen layers. */ |
| 50 | void resetLayers(); |
| 51 | |
Brian Salomon | 31fddc3 | 2021-04-30 13:08:55 -0400 | [diff] [blame] | 52 | /** |
| 53 | * Forces all offscreen layers to re-render the next time they're required for a frame but |
| 54 | * preserves the backing stores for them if already allocated. |
| 55 | */ |
| 56 | void rewindLayers(); |
| 57 | |
| 58 | /** |
| 59 | * Creates backing stores for any offscreen layers using the passed canvas's makeSurface(). |
| 60 | * Existing layers that match the canvas's recording context are not reallocated or rewound. |
| 61 | */ |
| 62 | void allocateLayers(SkCanvas*); |
| 63 | |
Brian Salomon | 33ec6113 | 2021-05-12 12:03:02 -0400 | [diff] [blame] | 64 | /** |
| 65 | * A set of IDs of offscreen layers in no particular order. If frame value >= 0 is specified |
| 66 | * then the layer set is filtered to layers used by that frame (or empty if >= numFrames). If |
| 67 | * < 0 then gathers all the layers across all frames. |
| 68 | */ |
| 69 | std::vector<int> layerIDs(int frame = -1) const; |
| 70 | |
| 71 | /** |
| 72 | * Gets the contents of an offscreen layer. It's contents will depend on current playback state |
| 73 | * (playFrame(), updateFrameLayers(), resetLayers()). If the layer currently has no backing |
| 74 | * store because it hasn't been drawn or resetLayers() was called then this will return nullptr. |
| 75 | * Layer contents are not affected by rewindLayers() as that simply lazily redraws the frame |
| 76 | * contents the next time it is required by playFrame*() or updateFrameLayers(). |
| 77 | */ |
| 78 | sk_sp<SkImage> layerSnapshot(int layerID) const; |
| 79 | |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 80 | private: |
| 81 | MSKPPlayer() = default; |
| 82 | // noncopyable, nonmoveable. |
| 83 | MSKPPlayer(const MSKPPlayer&) = delete; |
| 84 | MSKPPlayer(MSKPPlayer&&) = delete; |
| 85 | MSKPPlayer& operator=(const MSKPPlayer&) = delete; |
| 86 | MSKPPlayer& operator=(MSKPPlayer&&) = delete; |
| 87 | |
| 88 | // Cmds are used to draw content to the frame root layer and to offscreen layers. |
| 89 | struct Cmd; |
| 90 | // Draws a SkPicture. |
| 91 | struct PicCmd; |
| 92 | // Draws another layer. Stores the ID of the layer to draw and what command index on that |
| 93 | // layer should be current when the layer is drawn. The layer contents are updated to the |
| 94 | // stored command index before the layer is drawn. |
| 95 | struct DrawLayerCmd; |
| 96 | |
| 97 | // The commands for a root/offscreen layer and dimensions of the layer. |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 98 | struct LayerCmds { |
| 99 | LayerCmds() = default; |
| 100 | LayerCmds(LayerCmds&&) = default; |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 101 | SkISize fDimensions; |
| 102 | std::vector<std::unique_ptr<Cmd>> fCmds; |
| 103 | }; |
| 104 | |
| 105 | // Playback state of layer: the last command index drawn to it and the SkSurface with contents. |
| 106 | struct LayerState { |
| 107 | size_t fCurrCmd = -1; |
| 108 | sk_sp<SkSurface> fSurface; |
| 109 | }; |
| 110 | |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 111 | static sk_sp<SkSurface> MakeSurfaceForLayer(const LayerCmds&, SkCanvas* rootCanvas); |
Brian Salomon | 31fddc3 | 2021-04-30 13:08:55 -0400 | [diff] [blame] | 112 | |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 113 | void collectReferencedLayers(const LayerCmds& layer, std::vector<int>*) const; |
Brian Salomon | 33ec6113 | 2021-05-12 12:03:02 -0400 | [diff] [blame] | 114 | |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 115 | // MSKP layer ID -> LayerCmds |
| 116 | using LayerMap = std::unordered_map<int, LayerCmds>; |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 117 | // MSKP layer ID -> LayerState |
| 118 | using LayerStateMap = std::unordered_map<int, LayerState>; |
| 119 | |
| 120 | /** |
| 121 | * A SkCanvas that consumes the SkPicture and records Cmds into a Layer. It will spawn |
| 122 | * additional Layers and record nested SkPictures into those using additional CmdRecordCanvas |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 123 | * CmdRecordCanvas instances. It needs access to fOffscreenLayers to create and update LayerCmds |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 124 | * structs for offscreen layers. |
| 125 | */ |
| 126 | class CmdRecordCanvas; |
| 127 | |
| 128 | SkISize fMaxDimensions = {0, 0}; // Max dimensions across all frames. |
| 129 | LayerMap fOffscreenLayers; // All the offscreen layers for all frames. |
| 130 | LayerStateMap fOffscreenLayerStates; // Current surfaces and command idx for offscreen |
| 131 | // layers |
Brian Osman | 68556bc | 2021-07-30 15:24:22 -0400 | [diff] [blame] | 132 | std::vector<LayerCmds> fRootLayers; // One root layer for each frame. |
Brian Salomon | 06c9e29 | 2021-04-29 14:10:23 -0400 | [diff] [blame] | 133 | }; |
| 134 | |
| 135 | #endif |