Skip to content

RFC: add a scene-referred halation module - #22150

Draft
andriiryzhkov wants to merge 1 commit into
darktable-org:masterfrom
andriiryzhkov:iop_halation
Draft

RFC: add a scene-referred halation module#22150
andriiryzhkov wants to merge 1 commit into
darktable-org:masterfrom
andriiryzhkov:iop_halation

Conversation

@andriiryzhkov

Copy link
Copy Markdown
Collaborator

Draft/RFC. Adds a halation module: the warm bleed film gets when light passes through the emulsion, reflects off the base and re-exposes it from behind.

Related: #21235

darktable already spreads light from highlights, but both modules that do it run after the tone mapper – bloom at pipe order 61, soften (the Orton module) at 66. Halation happens at exposure, so it has to act on scene-linear energy, and it is chromatic: the red layer lies against the base, so red both travels furthest and is re-exposed most. bloom works in Lab and soften through HSL, so neither can spread one channel further than another. This sits at 28.6, after colorin and before the tone mappers, and derives a blur radius and a gain per channel from one "chromatic spread" control.

On whether blur + colorize already covers this (#21235): it gets you a coloured glow, but a tinted blur is one layer times one colour, so the halo's colour is flat with distance. Film gives a nearly neutral core that warms outward – measured on a neutral highlight, R/B here runs about 1.0 at 8px to 5 at 36px. That needs per-channel radii and gains, which no combination of existing modules can express.

On SpektraFilm, two separate things. Overlap: little. #21967 runs at 46.7, after the tone mappers, because it simulates the whole negative/print/scan chain and does its own tone rendering; this one composes with whatever tone mapper you use. Reuse of its halation code: sf_halation() is properly factored into src/common/, but it takes eighteen film-physical numbers normally derived from a chosen stock, so mapping four sliders onto them means owning that mapping anyway – in exchange for a hard dependency on an unmerged 15k-line PR. If SpektraFilm lands I would gladly converge on a shared scatter primitive under a neutral name.

Exercised in darkroom on backlit and high-contrast frames; not yet checked on a tiled export. No process_cl – the filter is O(1) per pixel. No RELEASE_NOTES.md entry while this is a draft.

Open questions. The taper constants – how much narrower and weaker green and blue get at full chromatic spread – are tuned by eye, not derived from film measurements. The pipe position of 28.6 is arbitrary in its ordering against diffuse. And whether it belongs in workflow: scene-referred's effect group by default, which is where I have put it.

Written with AI assistance.

@andriiryzhkov andriiryzhkov added feature: new new features to add scope: image processing correcting pixels labels Sep 4, 2026
@andriiryzhkov
andriiryzhkov marked this pull request as draft September 4, 2026 15:51
@piratenpanda

piratenpanda commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

I think shared scatter, halation and grain code for other standalone modules would be nice to have. Also maybe reuse code for another mode for negadoctor when official spektrafilm moves this code path to stable .

@andriiryzhkov

Copy link
Copy Markdown
Collaborator Author

@piratenpanda : Strongly in favour – shared scatter, halation and grain would be good for the tree, and a negadoctor mode is a natural fit.

One request while it's still cheap to change: name the shared primitives after the algorithm rather than the project. The precedent in src/common/ is consistent – dwt_ is used by diffuse, rawdenoise, retouch and eaw; eaw_ by atrous and denoiseprofile; heal_ by retouch. Each names an algorithm family, so the next person writing a module knows to look there. sf_ names the project, which reads as SpektraFilm's internals even though the code is general.

sf_gauss_yvv_coeffs is the clearest case: Young–van Vliet has nothing to do with film simulation, and dt_gaussian already lives in src/common/gaussian.c. Anyone needing a recursive Gaussian would never find it under that name.

Renaming is trivial while the PR is open.

darktable's two glow modules run after the tone mapper: bloom at pipe
order 61, soften at 66. Halation happens at exposure, so it belongs on
scene-linear data, and it is chromatic: the red layer lies against the
film base, so red both travels furthest and is re-exposed most. bloom
works in Lab and soften through HSL, so neither can spread one channel
further than another.

The module sits at 28.6 and derives a radius and a gain per channel
from one control. The blur is three weighted dt_gaussian passes: an
exponential is a scale mixture of Gaussians, and a separable
exponential would halate a point light as a cross.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: new new features to add scope: image processing correcting pixels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants