Add eight effects that throw something chaotic into the chain
Slit shift, channel split, pixel sort, corruption, shatter, warp, bit crush and feedback: tearing bands sideways, knocking the colour planes out of register, sorting runs of pixels, damaging blocks the way a broken stream does, breaking the picture into shards, pushing it along a noise field, mangling its bytes and laying it over itself again and again. All eight are pure transforms. None needs sources, so any of them can lead a chain, and like blur they start from a crop of a random source when they do and photographs are loaded. canvasFromSources holds that start once for them. Each draws its whole plan from the rng before touching a pixel, and pixel sort hands every line a seed of its own, so the parallel work never decides anything. The rotations in shatter and feedback take their sine and cosine from a short series of plain multiplications rather than libm, so a seed lands on the same pixels whatever the machine's maths library makes of std::sin. The tests hold every one of them to the same things -- a seed reproduces it, it runs without sources and as a later stage, a sliver of a canvas does not trip it, turned right down it leaves its input alone -- plus a check of what each actually does, and the registry now checks that no two effects declare the same param key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "core/image.hpp"
|
||||
#include "core/parallel.hpp"
|
||||
#include "core/rng.hpp"
|
||||
|
||||
namespace effector {
|
||||
|
||||
@@ -35,4 +37,20 @@ inline void fillFromSource(Image& canvas, const Image& src, int sx, int sy) {
|
||||
});
|
||||
}
|
||||
|
||||
// The canvas a pure transform starts from: a crop of a randomly chosen source,
|
||||
// or black when there is none. Inside a chain the only source is the stage
|
||||
// before, which has the canvas size, so no random value is drawn at all; only a
|
||||
// transform leading a chain with photographs loaded spends any of the rng here.
|
||||
inline Image canvasFromSources(const std::vector<Image>& sources, int width, int height, Rng& rng) {
|
||||
Image canvas(width, height);
|
||||
if (!sources.empty()) {
|
||||
const std::size_t index = 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);
|
||||
}
|
||||
return canvas;
|
||||
}
|
||||
|
||||
} // namespace effector
|
||||
|
||||
Reference in New Issue
Block a user