Let effects declare whether they need source images
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>
This commit is contained in:
@@ -100,7 +100,6 @@ void blurVertical(const Image& src, Image& dst, int radius) {
|
||||
|
||||
Image BlurEffect::render(const std::vector<Image>& sources, int width, int height, Rng& rng,
|
||||
const ParamValues& values) const {
|
||||
if (sources.empty()) throw EffectError("No source images.");
|
||||
if (width < 1 || height < 1) throw EffectError("Canvas size must be at least 1x1 pixels.");
|
||||
|
||||
const int radius = static_cast<int>(std::max(1L, paramInt(values, "radius", 8)));
|
||||
@@ -108,15 +107,16 @@ Image BlurEffect::render(const std::vector<Image>& sources, int width, int heigh
|
||||
const std::string direction = paramChoice(values, "direction", "both");
|
||||
const bool firstSource = paramBool(values, "first_source", false);
|
||||
|
||||
// Every random value is drawn here, before any threading, so that a seed
|
||||
// always gives the same image.
|
||||
const std::size_t index = firstSource ? 0 : rng.below(sources.size() - 1);
|
||||
const Image& src = sources[index];
|
||||
const int sx = static_cast<int>(rng.inRange(0, std::max(0, src.width() - width)));
|
||||
const int sy = static_cast<int>(rng.inRange(0, std::max(0, src.height() - height)));
|
||||
|
||||
Image canvas(width, height);
|
||||
fillFromSource(canvas, src, sx, sy);
|
||||
if (!sources.empty()) {
|
||||
// Every random value is drawn here, before any threading, so that a
|
||||
// seed always gives the same image.
|
||||
const std::size_t index = firstSource ? 0 : rng.below(sources.size() - 1);
|
||||
const Image& src = sources[index];
|
||||
const int sx = static_cast<int>(rng.inRange(0, std::max(0, src.width() - width)));
|
||||
const int sy = static_cast<int>(rng.inRange(0, std::max(0, src.height() - height)));
|
||||
fillFromSource(canvas, src, sx, sy);
|
||||
}
|
||||
|
||||
Image scratch(width, height);
|
||||
for (int pass = 0; pass < passes; ++pass) {
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
|
||||
namespace effector {
|
||||
|
||||
// Fills the canvas with one of the sources and softens it with a separable box
|
||||
// blur, repeated a few times to approximate a gaussian.
|
||||
// Softens its input with a separable box blur, repeated a few times to
|
||||
// approximate a gaussian. It needs no source images of its own: given none it
|
||||
// blurs an empty canvas, which is what makes it usable as a pure filter stage.
|
||||
class BlurEffect : public Effect {
|
||||
public:
|
||||
std::string name() const override { return "blur"; }
|
||||
std::string title() const override { return "Blur"; }
|
||||
std::string description() const override {
|
||||
return "Fills the canvas from a randomly chosen source and softens it with a repeated "
|
||||
"box blur, horizontally, vertically or both.";
|
||||
return "Softens its input with a repeated box blur, horizontally, vertically or both; "
|
||||
"with sources loaded it fills the canvas from a randomly chosen one first.";
|
||||
}
|
||||
const std::vector<ParamSpec>& params() const override;
|
||||
bool needsSources() const override { return false; }
|
||||
|
||||
Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
|
||||
const ParamValues& values) const override;
|
||||
|
||||
Reference in New Issue
Block a user