Add Focus pull, a video effect that pulls Blur in and out of focus
Blur's box blur moves to effects/box_blur.hpp so both share it; Blur renders exactly as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+2
-64
@@ -1,10 +1,9 @@
|
||||
#include "effects/blur.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include "core/parallel.hpp"
|
||||
#include "effects/box_blur.hpp"
|
||||
#include "effects/source_fill.hpp"
|
||||
|
||||
namespace effector {
|
||||
@@ -23,57 +22,6 @@ const std::vector<ParamSpec>& BlurEffect::params() const {
|
||||
return specs;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// One separable box blur pass. `stride` is the distance in bytes between two
|
||||
// neighbouring samples along the blurred axis, `count` how many of them there
|
||||
// are; the window is clamped at both ends.
|
||||
void boxPass(const std::uint8_t* in, std::uint8_t* out, int count, std::size_t stride, int radius) {
|
||||
const int window = 2 * radius + 1;
|
||||
int sum[3] = {0, 0, 0};
|
||||
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
sum[c] = in[c] * (radius + 1);
|
||||
for (int i = 1; i <= radius; ++i) {
|
||||
sum[c] += in[static_cast<std::size_t>(std::min(i, count - 1)) * stride + c];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
out[static_cast<std::size_t>(i) * stride + c] =
|
||||
static_cast<std::uint8_t>(sum[c] / window);
|
||||
}
|
||||
const int add = std::min(i + radius + 1, count - 1);
|
||||
const int drop = std::max(i - radius, 0);
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
sum[c] += in[static_cast<std::size_t>(add) * stride + c] -
|
||||
in[static_cast<std::size_t>(drop) * stride + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blurHorizontal(const Image& src, Image& dst, int radius) {
|
||||
parallelFor(static_cast<std::size_t>(src.height()), [&](std::size_t begin, std::size_t end) {
|
||||
for (std::size_t y = begin; y < end; ++y) {
|
||||
boxPass(src.row(static_cast<int>(y)), dst.row(static_cast<int>(y)), src.width(), 3,
|
||||
radius);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void blurVertical(const Image& src, Image& dst, int radius) {
|
||||
const std::size_t stride = src.stride();
|
||||
parallelFor(static_cast<std::size_t>(src.width()), [&](std::size_t begin, std::size_t end) {
|
||||
for (std::size_t x = begin; x < end; ++x) {
|
||||
const std::size_t offset = x * 3;
|
||||
boxPass(src.data() + offset, dst.data() + offset, src.height(), stride, radius);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Image BlurEffect::render(const std::vector<Image>& 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.");
|
||||
@@ -94,17 +42,7 @@ Image BlurEffect::render(const std::vector<Image>& sources, int width, int heigh
|
||||
fillFromSource(canvas, src, sx, sy);
|
||||
}
|
||||
|
||||
Image scratch(width, height);
|
||||
for (int pass = 0; pass < passes; ++pass) {
|
||||
if (direction != "vertical") {
|
||||
blurHorizontal(canvas, scratch, std::min(radius, std::max(1, width - 1)));
|
||||
std::swap(canvas, scratch);
|
||||
}
|
||||
if (direction != "horizontal") {
|
||||
blurVertical(canvas, scratch, std::min(radius, std::max(1, height - 1)));
|
||||
std::swap(canvas, scratch);
|
||||
}
|
||||
}
|
||||
boxBlur(canvas, radius, passes, direction);
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user