Whether an effect works from the loaded photographs is now part of the declarative plug-in contract, next to params(): Effect::needsSources() defaults to true, and blur — which only softens what it is handed — says no and blurs an empty canvas when given nothing. Only the first stage of a cascade ever sees the sources, so chainNeedsSources() just asks the first stage. Both front-ends gate on that instead of on "are there any images": the CLI only refuses an empty batch when the chain wants one, and the interface renders a blur-led chain with no images loaded, saying whichever thing is actually missing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
1.7 KiB
C++
55 lines
1.7 KiB
C++
#include "core/chain.hpp"
|
|
|
|
namespace effector {
|
|
|
|
Image renderChain(const std::vector<ChainStage>& stages, const std::vector<Image>& sources,
|
|
int width, int height, Rng& rng) {
|
|
if (stages.empty()) throw EffectError("The effect chain is empty.");
|
|
|
|
Image image;
|
|
for (std::size_t i = 0; i < stages.size(); ++i) {
|
|
const auto& stage = stages[i];
|
|
if (!stage.effect) throw EffectError("The effect chain has an empty stage.");
|
|
|
|
auto values = stage.effect->defaults();
|
|
for (const auto& [key, value] : stage.values) values[key] = value;
|
|
|
|
if (i == 0) {
|
|
image = stage.effect->render(sources, width, height, rng, values);
|
|
} else {
|
|
// Feeding the previous result back in as the only source is what
|
|
// cascading means here; effects need to know nothing about it.
|
|
const std::vector<Image> previous = {std::move(image)};
|
|
image = stage.effect->render(previous, width, height, rng, values);
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
|
|
bool chainNeedsSources(const std::vector<ChainStage>& stages) {
|
|
if (stages.empty() || !stages.front().effect) return false;
|
|
return stages.front().effect->needsSources();
|
|
}
|
|
|
|
std::string chainTitle(const std::vector<ChainStage>& stages) {
|
|
std::string text;
|
|
for (const auto& stage : stages) {
|
|
if (!stage.effect) continue;
|
|
if (!text.empty()) text += " → ";
|
|
text += stage.effect->title();
|
|
}
|
|
return text;
|
|
}
|
|
|
|
std::string chainSlug(const std::vector<ChainStage>& stages) {
|
|
std::string text;
|
|
for (const auto& stage : stages) {
|
|
if (!stage.effect) continue;
|
|
if (!text.empty()) text += "-";
|
|
text += stage.effect->name();
|
|
}
|
|
return text;
|
|
}
|
|
|
|
} // namespace effector
|