Effector: C++/CMake image effects app

Port of the Python prototype to C++20 with a CMake build:

- effector_core: GUI-free static library (effects, image IO via gdk-pixbuf,
  declarative param specs, registry, portable seeded RNG, thread helper)
- scanlines effect: plans strips from the seeded RNG, then copies pixels in
  parallel with memcpy and doubling horizontal tiling
- gtkmm 4 interface: native file dialogs, widgets generated from the param
  specs, coalesced background renders, preview as a GDK memory texture
- CLI: flags generated from the effect registry, headless rendering with no
  display required
- ctest suite covering the RNG, registry, effect geometry and image round trip

The Python prototype is kept under python-reference/.
This commit is contained in:
hans
2026-09-18 14:28:13 +02:00
commit 925a719da0
36 changed files with 2859 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace effector {
// Tightly packed 8-bit RGB. Row stride is always width * 3, which lets the
// effects work with plain memcpy and hands GDK a buffer it can upload as is.
class Image {
public:
Image() = default;
Image(int width, int height);
int width() const noexcept { return width_; }
int height() const noexcept { return height_; }
std::size_t stride() const noexcept { return static_cast<std::size_t>(width_) * 3; }
bool empty() const noexcept { return width_ <= 0 || height_ <= 0; }
std::uint8_t* row(int y) noexcept { return pixels_.data() + static_cast<std::size_t>(y) * stride(); }
const std::uint8_t* row(int y) const noexcept {
return pixels_.data() + static_cast<std::size_t>(y) * stride();
}
std::uint8_t* data() noexcept { return pixels_.data(); }
const std::uint8_t* data() const noexcept { return pixels_.data(); }
std::size_t byteSize() const noexcept { return pixels_.size(); }
const std::string& path() const noexcept { return path_; }
void setPath(std::string path) { path_ = std::move(path); }
// Decodes via GdkPixbuf (PNG, JPEG, WebP, TIFF, BMP, ...), applying the
// embedded EXIF orientation. Throws std::runtime_error on failure.
static Image load(const std::string& path);
// Encodes by file extension; png is used when the extension is unknown.
void save(const std::string& path) const;
private:
int width_ = 0;
int height_ = 0;
std::vector<std::uint8_t> pixels_;
std::string path_;
};
struct LoadFailure {
std::string path;
std::string message;
};
// Loads every path, collecting rather than throwing on individual failures.
std::vector<Image> loadImages(const std::vector<std::string>& paths,
std::vector<LoadFailure>& failures);
} // namespace effector