#pragma once #include #include #include #include #include "core/image.hpp" #include "core/params.hpp" #include "core/rng.hpp" namespace effector { class EffectError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; // Where a render sits in time. The default -- no frame rate -- is a still // picture; a video hands every frame its index, the clip's length in frames // and its rate, so an effect that moves works out where it is from these // alone and a frame can be rendered on its own, in any order. struct FrameTime { int index = 0; int count = 1; int fps = 0; bool video() const noexcept { return fps > 0; } double seconds() const noexcept { return video() ? static_cast(index) / fps : 0.0; } }; // Base class for every effect. Canvas size and the random source are common to // all of them; params() covers only the effect-specific knobs. class Effect { public: virtual ~Effect() = default; virtual std::string name() const = 0; virtual std::string title() const = 0; virtual std::string description() const = 0; virtual const std::vector& params() const = 0; // Whether the effect builds its image out of the loaded source images. // Effects that only transform what they are handed (blur) say no, and a // chain that starts with one of them needs no sources at all. virtual bool needsSources() const { return true; } // The pool of named styles this effect's styled params are drawn from, or // empty when it takes no style. Effects that answer the same kind share // one pool, so a stage can be pointed at a look its neighbours already // use. An effect with no kind must declare no styled params. virtual std::string styleKind() const { return {}; } // How a stage of this effect is named in a list of stages, given its // values: the title unless the values say something more telling. virtual std::string label(const ParamValues& values) const { (void)values; return title(); } // Whether this is a video effect rather than an image effect. An image // effect makes a picture and holds still in a clip; a video effect is // motion and nothing else, so it is offered only in a video project and a // still skips it whole, as it skips a stage that is switched off. Video // effects derive from VideoEffect (effects/video_effect.hpp), which // answers true here and keeps the rules a moving stage has to follow. virtual bool video() const { return false; } virtual Image render(const std::vector& sources, int width, int height, Rng& rng, const ParamValues& values) const = 0; // One frame of a video, or a still when `frame` says so. An image effect // needs only render(), which this falls back to; a video effect renders // here, and must leave `rng` in the same state on every frame -- a // frame's own randomness comes from a generator of its own -- so the // stages after it stay put while it moves. virtual Image renderFrame(const std::vector& sources, int width, int height, Rng& rng, const ParamValues& values, const FrameTime& frame) const { (void)frame; return render(sources, width, height, rng, values); } ParamValues defaults() const; }; using EffectPtr = std::shared_ptr; } // namespace effector