Load source images in the background with a progress window

Decoding a batch of photographs took seconds on the main thread, so the
window froze with no sign of what was happening. loadImages now takes an
optional LoadProgress (per-file callback plus a cancel flag) and hands files
out from a shared cursor, so one large file cannot stall the queue behind it.

The GUI decodes off the main thread and shows a progress window with the
count, the current file and a Cancel button; it only appears if the batch is
still running after 250 ms. Headless mode keeps a "reading n/total" line on
stderr when it is attached to a terminal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hans
2026-09-18 14:39:07 +02:00
co-authored by Claude Opus 5
parent 925a719da0
commit 7e98357070
7 changed files with 317 additions and 15 deletions
+20 -1
View File
@@ -1,7 +1,11 @@
#include <unistd.h>
#include <chrono>
#include <cstdio>
#include <filesystem>
#include <iomanip>
#include <iostream>
#include <mutex>
#include "cli/options.hpp"
#include "core/effect.hpp"
@@ -16,7 +20,22 @@ int renderHeadless(const effector::Options& options) {
const auto effect = effector::effectByName(options.effect);
std::vector<effector::LoadFailure> failures;
auto sources = effector::loadImages(options.images, failures);
// Decoding a batch of photographs takes seconds; on a terminal keep a
// single line updated so the wait is not silent. Redirected output stays
// clean, since the line would only clutter a log.
effector::LoadProgress progress;
std::mutex progressMutex;
const bool showProgress = isatty(fileno(stderr)) != 0 && options.images.size() > 1;
if (showProgress) {
progress.onFile = [&](std::size_t done, std::size_t total, const std::string& path) {
std::lock_guard<std::mutex> lock(progressMutex);
std::cerr << "\r\033[Kreading " << done << "/" << total << " "
<< std::filesystem::path(path).filename().string() << std::flush;
};
}
auto sources = effector::loadImages(options.images, failures, showProgress ? &progress : nullptr);
if (showProgress) std::cerr << "\r\033[K" << std::flush;
for (const auto& failure : failures) {
std::cerr << "effector: skipping " << failure.path << ": " << failure.message << "\n";
}