blob: bb23a52352323daa61b3abc8d8f6b3da5bd18b02 [file] [edit]
#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