Files
effector/src/core/effect.hpp
T
SiandClaude Opus 5.5 cdaadffc89 Split effects into image and video effects, with VHS tracking as the first video effect
Effect::video() says which kind an effect is. The Add effect menu lists the
two kinds in sections of their own, each scrolling on its own, and offers
video effects only in a video project; a video stage carries a film icon in
the chain and, in a still, is dimmed and skipped like a stage switched off.
enabledStages takes whether the render is a video and is the one place that
leaves video stages out of a still. --help groups the video effects under a
heading of their own, --list-effects marks them, and a video effect given
for a still is skipped with a warning.

VideoEffect is the base every video effect builds on, and keeps the rules a
moving stage has to follow: it takes one value from the shared rng, the same
on every frame, and hands the effect steady and per-frame generators and
loop-rounded time through a Moment, so a subclass writes only animate().
VHS tracking is the first, and the one to copy: the colour planes sway and
shake and a tracking bar rolls down the picture.

That motion moves out of channel split, which is an image effect again and
renders stills exactly as before; forVideo() goes with it. Version 0.7.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:03:39 +02:00

89 lines
3.4 KiB
C++

#pragma once
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#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<double>(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<ParamSpec>& 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<Image>& 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<Image>& 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<const Effect>;
} // namespace effector