Add Sort sweep, a video effect that sweeps Pixel sort's window over the brightness
The run sorting moves to effects/sort_runs.hpp so both share it; Pixel sort renders exactly as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "core/parallel.hpp"
|
||||
#include "effects/sampling.hpp"
|
||||
#include "effects/sort_runs.hpp"
|
||||
#include "effects/source_fill.hpp"
|
||||
|
||||
namespace effector {
|
||||
@@ -34,39 +35,6 @@ const std::vector<ParamSpec>& PixelSortEffect::params() const {
|
||||
|
||||
namespace {
|
||||
|
||||
int luma(const std::uint8_t* px) { return (px[0] * 54 + px[1] * 183 + px[2] * 19) >> 8; }
|
||||
|
||||
// Hue on a 0..1535 scale (six sectors of 256), in integers.
|
||||
int hue(const std::uint8_t* px) {
|
||||
const int r = px[0], g = px[1], b = px[2];
|
||||
const int hi = std::max({r, g, b});
|
||||
const int lo = std::min({r, g, b});
|
||||
const int range = hi - lo;
|
||||
if (range == 0) return 0;
|
||||
if (hi == r) return g >= b ? (g - b) * 255 / range : 1535 - (b - g) * 255 / range;
|
||||
if (hi == g) return 512 + (b - r) * 255 / range;
|
||||
return 1024 + (r - g) * 255 / range;
|
||||
}
|
||||
|
||||
int saturation(const std::uint8_t* px) {
|
||||
return std::max({px[0], px[1], px[2]}) - std::min({px[0], px[1], px[2]});
|
||||
}
|
||||
|
||||
// The image with rows and columns swapped, so vertical sorting is horizontal
|
||||
// sorting of the transpose.
|
||||
Image transposed(const Image& src) {
|
||||
Image out(src.height(), src.width());
|
||||
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) {
|
||||
std::uint8_t* row = out.row(static_cast<int>(x));
|
||||
for (int y = 0; y < src.height(); ++y) {
|
||||
std::memcpy(row + static_cast<std::size_t>(y) * 3, src.row(y) + x * 3, 3);
|
||||
}
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
struct LinePlan {
|
||||
bool sorted = false;
|
||||
std::uint64_t seed = 0; // for the run lengths, when they are limited
|
||||
@@ -99,49 +67,19 @@ Image PixelSortEffect::render(const std::vector<Image>& sources, int width, int
|
||||
if (maxRun > 0) line.seed = rng.next();
|
||||
}
|
||||
|
||||
auto keyOf = [&](const std::uint8_t* px) {
|
||||
if (keyName == "hue") return hue(px);
|
||||
if (keyName == "saturation") return saturation(px);
|
||||
return luma(px);
|
||||
};
|
||||
SortRule rule;
|
||||
rule.low = low;
|
||||
rule.high = high;
|
||||
rule.key = sortKeyNamed(keyName);
|
||||
rule.reverse = reverse;
|
||||
rule.maxRun = maxRun;
|
||||
|
||||
parallelFor(static_cast<std::size_t>(lines), [&](std::size_t begin, std::size_t end) {
|
||||
std::vector<std::pair<int, std::array<std::uint8_t, 3>>> run;
|
||||
for (std::size_t line = begin; line < end; ++line) {
|
||||
if (!plan[line].sorted) continue;
|
||||
Rng runRng(plan[line].seed);
|
||||
std::uint8_t* row = canvas.row(static_cast<int>(line));
|
||||
|
||||
auto flush = [&](int start) {
|
||||
// A stable sort, so equal keys keep their order on every
|
||||
// standard library.
|
||||
std::stable_sort(run.begin(), run.end(), [&](const auto& a, const auto& b) {
|
||||
return reverse ? a.first > b.first : a.first < b.first;
|
||||
});
|
||||
for (std::size_t i = 0; i < run.size(); ++i) {
|
||||
std::memcpy(row + (static_cast<std::size_t>(start) + i) * 3, run[i].second.data(), 3);
|
||||
}
|
||||
run.clear();
|
||||
};
|
||||
|
||||
int start = 0;
|
||||
int limit = maxRun > 0 ? static_cast<int>(runRng.inRange(1, maxRun)) : span;
|
||||
for (int x = 0; x < span; ++x) {
|
||||
const std::uint8_t* px = row + static_cast<std::size_t>(x) * 3;
|
||||
const int brightness = luma(px);
|
||||
if (brightness < low || brightness > high) {
|
||||
flush(start);
|
||||
start = x + 1;
|
||||
continue;
|
||||
}
|
||||
if (static_cast<int>(run.size()) >= limit) {
|
||||
flush(start);
|
||||
start = x;
|
||||
if (maxRun > 0) limit = static_cast<int>(runRng.inRange(1, maxRun));
|
||||
}
|
||||
run.push_back({keyOf(px), {px[0], px[1], px[2]}});
|
||||
}
|
||||
flush(start);
|
||||
sortLine(canvas.row(static_cast<int>(line)), span, rule, runRng, run);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user