Files
effector/src/effects/monochrome.hpp
T
SiandClaude Opus 5 efee1cf224 Add a black and white effect
Monochrome is the fourth effect and the third that declares
needsSources() false, so it works as a filter stage at the end of a
cascade and can also lead one, filling the canvas from a source the way
blur and grain do. Its knobs are the channel weighting, the contrast, a
toner and the strength: the weighting offers the mixes a photographer
would make behind the lens, so the colour names weigh that channel most
and "red" darkens a sky the way a red filter does; contrast pulls the
greys away from mid grey; the toners tint the finished greys warm
(sepia), barely (selenium) or cold (cyanotype); and strength mixes the
result back over the colour input, which at 0 is a pass through.

Contrast and toner only ever see one grey, so the whole tail of the
conversion is folded into a 256-entry table per channel, built once
before the parallel pass; the per-pixel work is then integer only, with
the weights as 1024ths and the tints as 256ths. The Rng is touched only
for the source pick and the crop offset, serially, before any threading.

The tests gain a `monochrome` section: neutrality of the output, each
weighting measured on a pure red field, the contrast pivot at mid grey,
both toner directions, strength as a pass through and as a midpoint, and
the sourceless render that stays black.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 16:55:00 +02:00

29 lines
1.2 KiB
C++

#pragma once
#include "core/effect.hpp"
namespace effector {
// Converts its input to black and white, with the choice of channel weighting
// a photographer would make behind the lens (a red filter darkening a sky, for
// instance), an optional contrast pull and an optional toner. Like the blur it
// needs no source images of its own, so it can lead a chain; given sources it
// fills the canvas from one of them first.
class MonochromeEffect : public Effect {
public:
std::string name() const override { return "monochrome"; }
std::string title() const override { return "Black and white"; }
std::string description() const override {
return "Converts its input to black and white with a choice of channel weighting, an "
"optional contrast pull and an optional toner; with sources loaded it fills the "
"canvas from a randomly chosen one first.";
}
const std::vector<ParamSpec>& params() const override;
bool needsSources() const override { return false; }
Image render(const std::vector<Image>& sources, int width, int height, Rng& rng,
const ParamValues& values) const override;
};
} // namespace effector