A picture with a title, a caption and a credit meant typing the same
font, size and colour into every text stage, and changing the look meant
typing them all again. A project now holds named styles: a stage follows
one, and editing it moves every stage that follows it at once.
The front-ends learn nothing effect-specific. A ParamSpec can be marked
.styled(), saying a style carries it rather than the stage, and
Effect::styleKind() names the pool it comes from -- so styleSpecs(kind)
is to a style what params() is to a stage, and a second text-ish effect
would slot into the styles that already exist. The look is what a style
carries; the words and the placement stay on the stage, which is what
lets two stages share a typography in two different corners.
stageValues() resolves one in a single place, and a stage keeps a full
set of values of its own beside the style, so switching it to Custom,
dropping the style, or opening the project in a build that does not know
it renders exactly the same picture. A style moves stages together; it
never holds the only copy of anything.
The command line builds no styles and passes none, so it renders what it
always did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Writes a line or several over its input in a chosen family, weight, size
and colour, optionally on a box filled with a colour or with a picture
from disk. Like the blur and the overlay it needs no sources of its own,
so it can lead a chain; given sources it fills the canvas from a randomly
chosen one first.
Two param types rather than effect-specific branches in the front-ends:
Text, free-form text the user types, and Color, a colour written
"#rrggbb". Both travel as strings, so the interface gets an entry and a
colour button, the command line gets a flag (a colour that does not parse
is refused there rather than drawn as black), and a project keeps them
across a round trip.
The glyphs come from Pango, which draws through Cairo without a display,
so the core stays headless — but they are the one thing here that is not
byte-identical across machines: a machine without the chosen family
renders the substitute its fontconfig picks. Everything the effect is
handed, the rng included, is used exactly as every other effect uses it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lays a single picture from disk over its input: fitted, filled, stretched,
left at its own size or tiled, centred or pinned to the top left corner,
nudged by an offset and mixed in by its opacity.
The file it needs is a new kind of param, Path, so the interface shows a
chooser and the CLI takes a file name, both without knowing an overlay
exists. Projects write it relative to their own folder, as they do a
source, and Image::load can now hand back the alpha channel instead of
flattening it onto white, so a cut-out PNG keeps its cut-out.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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/.