#include "effects/blur.hpp" #include #include #include "effects/box_blur.hpp" #include "effects/source_fill.hpp" namespace effector { const std::vector& BlurEffect::params() const { static const std::vector specs = { ParamSpec::makeInt("radius", "Radius", 8, 1, 256, "Half width in pixels of the blur window."), ParamSpec::makeInt("passes", "Passes", 2, 1, 8, "How often the box blur is repeated; more passes look more gaussian."), ParamSpec::makeChoice("direction", "Direction", "both", {"both", "horizontal", "vertical"}, "Axis the blur is applied along."), ParamSpec::makeBool("first_source", "Use the first image", false, "Always blur the first source instead of picking one at random."), }; return specs; } Image BlurEffect::render(const std::vector& sources, int width, int height, Rng& rng, const ParamValues& values) const { if (width < 1 || height < 1) throw EffectError("Canvas size must be at least 1x1 pixels."); const int radius = static_cast(std::max(1L, paramInt(values, "radius", 8))); const int passes = static_cast(std::max(1L, paramInt(values, "passes", 2))); const std::string direction = paramChoice(values, "direction", "both"); const bool firstSource = paramBool(values, "first_source", false); Image canvas(width, height); 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(rng.inRange(0, std::max(0, src.width() - width))); const int sy = static_cast(rng.inRange(0, std::max(0, src.height() - height))); fillFromSource(canvas, src, sx, sy); } boxBlur(canvas, radius, passes, direction); return canvas; } } // namespace effector