| #ifndef _RIVE_WATERMARK_HPP_ |
| #define _RIVE_WATERMARK_HPP_ |
| |
| #include "rive/math/aabb.hpp" |
| #include "rive/refcnt.hpp" |
| |
| #include <cstdint> |
| #include <memory> |
| |
| namespace rive |
| { |
| class ArtboardInstance; |
| class RenderPaint; |
| class RenderPath; |
| class Renderer; |
| class StateMachineInstance; |
| |
| /// A pre-roll played in front of the artboard instance it is attached to. While |
| /// it plays, the host artboard neither animates nor draws: the watermark is the |
| /// only thing advanced and the only thing on screen. Once it finishes the host |
| /// takes over and the watermark is released, so it never plays twice for the |
| /// same instance. |
| /// |
| /// Attached by File to the instances it vends when the file's manifest carries |
| /// a watermark section (see ManifestAsset::hasWatermark()). |
| class Watermark |
| { |
| public: |
| Watermark(std::unique_ptr<ArtboardInstance> artboard, |
| std::unique_ptr<StateMachineInstance> stateMachine); |
| ~Watermark(); |
| |
| /// Whether this frame belongs to the watermark rather than to the host. |
| /// True from the first advance until the owner releases the watermark, |
| /// which it does on the frame after the last one the watermark played. The |
| /// "has it started" part matters: a host that is never driven by a state |
| /// machine never starts its watermark, and must keep drawing itself rather |
| /// than freeze on a pre-roll that would never end. |
| bool isPlaying() const { return m_started; } |
| |
| /// How much of the pre-roll has actually been played. This is what the |
| /// clock budget bounds, so it is the thing worth asserting on. |
| float elapsedSeconds() const { return m_elapsedSeconds; } |
| |
| /// Advance the pre-roll. Returns true while the frame belongs to the |
| /// watermark, meaning the host must stay frozen. The frame on which the |
| /// watermark finishes still returns true, so its last authored frame gets |
| /// drawn; the handover happens on the next one. |
| /// |
| /// elapsedSeconds is a request rather than an instruction, see |
| /// clampElapsed(). |
| bool advance(float elapsedSeconds); |
| |
| /// Draw the watermark over an opaque backdrop covering the host's box. |
| /// |
| /// The backdrop is not decoration. The host is frozen but whatever it last |
| /// drew is still on the surface, and the watermark is fit inside the host's |
| /// box rather than filling it, so without one the file would show through |
| /// and around the pre-roll. |
| void draw(Renderer* renderer, const AABB& hostBounds); |
| |
| private: |
| /// How much of elapsedSeconds the pre-roll actually consumes. |
| /// |
| /// The host's delta is honored while the pre-roll's total consumption |
| /// stays within the real time that has actually passed (plus one small |
| /// tolerance), so playback stays smooth and a host with a better notion of |
| /// frame time (fixed timestep, vsync) still drives it. Beyond that it is |
| /// cut to what is left of the budget, which is how a watermark would |
| /// otherwise get skipped: one huge advance to seek past it, or a loop |
| /// advancing far faster than it draws. The budget is cumulative |
| /// deliberately -- a per-call comparison lets the tolerance be re-spent |
| /// every frame, so a loop of advances just under it costs nothing in real |
| /// time yet finishes the pre-roll. |
| /// |
| /// Under File::deterministicMode the host owns the timeline outright and |
| /// elapsedSeconds is taken verbatim, matching how ScrollPhysics and |
| /// TextInputListenerGroup treat time. |
| float clampElapsed(float elapsedSeconds); |
| |
| /// Fill [hostBounds] with the opaque backdrop, building (and caching) the |
| /// paint and path on first use. |
| void drawBackdrop(Renderer* renderer, const AABB& hostBounds); |
| |
| // Declared before m_stateMachine so it outlives it: Scene holds a raw, |
| // non-owning ArtboardInstance*, and members are destroyed in reverse. |
| std::unique_ptr<ArtboardInstance> m_artboard; |
| std::unique_ptr<StateMachineInstance> m_stateMachine; |
| // Monotonic clock in microseconds, sampled at construction and at every |
| // advance. |
| int64_t m_lastTimeMicros; |
| // Real time measured across every advance. Together with m_elapsedSeconds |
| // it is the pre-roll's spending limit: what it has consumed may not run |
| // ahead of what has actually elapsed, give or take one tolerance. |
| float m_wallSeconds = 0.0f; |
| // Backdrop paint and path, built lazily on the first draw. The path is |
| // rebuilt whenever the host's box changes, since a window can be resized |
| // mid pre-roll. |
| rcp<RenderPaint> m_backdropPaint; |
| rcp<RenderPath> m_backdropPath; |
| AABB m_backdropBounds; |
| float m_elapsedSeconds = 0.0f; |
| bool m_started = false; |
| bool m_finished = false; |
| }; |
| } // namespace rive |
| |
| #endif |