Picture film inside a mask layer

The manual described film in a mask layer (6b99f67) with no picture of
it, and the paragraph named the control `Print exposure` where the
panel says `Print Exposure`.

A new scene, film_local, sets Kodak Ektar 100 on an upright alpine
frame — a negative, so it is printed and has a print exposure — then
adds a linear gradient, turns it by its rotate handle to fall from the
top, moves it onto the sky, shows the mask as its edge so the print
stays visible, and raises the layer's Print Exposure to burn the sky
in. Done Masking, then Before held. It puts the masks back to Tint
before undoing, as the other scenes expect.

The picture follows the film list's, under the paragraph it
illustrates. The bundled manual is regenerated to match.
This commit is contained in:
2026-09-27 08:18:15 -04:00
parent 5bff453d37
commit be37218696
4 changed files with 73 additions and 6 deletions
+6 -3
View File
@@ -273,13 +273,16 @@ sensor does not. The list opens over the column and scrolls on its own — by
wheel, drag or flick, or with `Up`, `Down` and `Enter` — down to the
black-and-white stocks at its end.
![Opening the film list, scrolling it, choosing Velvia, then holding Before](media/film.gif)
The film's sliders work on a mask as they do on the whole photograph, the way
a printer dodges and burns: in a layer, `Print exposure` darkens or lightens
a printer dodges and burns: in a layer, `Print Exposure` darkens or lightens
that region of the print, `Push` develops it further, and the stock stays the
one the photograph was made on. Where layers overlap, the photograph takes the
average of what they ask for.
average of what they ask for. Below, a negative stock on an alpine frame, then
a gradient over the sky whose `Print Exposure` burns it in.
![Opening the film list, scrolling it, choosing Velvia, then holding Before](media/film.gif)
![Kodak Ektar 100 on the whole frame, a gradient turned to cover the sky, its Print Exposure raised to burn the sky in, then holding Before](media/film-local.gif)
### History, snapshots, presets
+5 -3
View File
@@ -332,12 +332,14 @@ named stock; below it, the print exposure and push controls a film has and a
sensor does not. The list opens over the column and scrolls on its own — by
wheel, drag or flick, or with <code>Up</code>, <code>Down</code> and <code>Enter</code> — down to the
black-and-white stocks at its end.</p>
<figure><img loading="lazy" src="media/film.gif" alt="Opening the film list, scrolling it, choosing Velvia, then holding Before"><figcaption>Opening the film list, scrolling it, choosing Velvia, then holding Before</figcaption></figure>
<p>The film's sliders work on a mask as they do on the whole photograph, the way
a printer dodges and burns: in a layer, <code>Print exposure</code> darkens or lightens
a printer dodges and burns: in a layer, <code>Print Exposure</code> darkens or lightens
that region of the print, <code>Push</code> develops it further, and the stock stays the
one the photograph was made on. Where layers overlap, the photograph takes the
average of what they ask for.</p>
<figure><img loading="lazy" src="media/film.gif" alt="Opening the film list, scrolling it, choosing Velvia, then holding Before"><figcaption>Opening the film list, scrolling it, choosing Velvia, then holding Before</figcaption></figure>
average of what they ask for. Below, a negative stock on an alpine frame, then
a gradient over the sky whose <code>Print Exposure</code> burns it in.</p>
<figure><img loading="lazy" src="media/film-local.gif" alt="Kodak Ektar 100 on the whole frame, a gradient turned to cover the sky, its Print Exposure raised to burn the sky in, then holding Before"><figcaption>Kodak Ektar 100 on the whole frame, a gradient turned to cover the sky, its Print Exposure raised to burn the sky in, then holding Before</figcaption></figure>
<h3 id="history-snapshots-presets">History, snapshots, presets</h3>
<p>Every change is a step; <code>Undo</code> and the History panel walk them. <code>Snapshot</code>
keeps the current state under a name. <code>Presets…</code> saves the settings to
Binary file not shown.
+59
View File
@@ -57,6 +57,7 @@ TEAPOT = '_MG_4918'
PANO_FIRST, PANO_LAST = '_MG_8320', '_MG_8331'
NY_FIRST, NY_LAST = '_MG_8393', '_MG_8397'
ALPS_ROW = ['_MG_8322', '_MG_8328'] # the second row of the alps, first and last
ALPS_SKY = '_MG_8330' # an upright alpine frame, its top third sky and cloud
TOWER = '_MG_8693' # towers shot looking up: verticals that converge
ROAD = '_MG_8672' # a road under a sky, with marks on it: masks and repair
ROAD_MARK = (0.305, 0.794) # a dark mark on its tarmac
@@ -1005,6 +1006,64 @@ def film():
undo_all()
FILM_LOCAL = 'Kodak Ektar 100' # a negative, so it is printed and has a print exposure
def choose_film(stock):
"""`stock` chosen from the film list, which is open at its top."""
px, py, _, _ = film_list_origin()
vx0, vy0, vx1, vy1 = dr.rect(f'{stock}@Text')
dr.click(px + (vx0 + vx1) // 2, py + (vy0 + vy1) // 2)
pause(3)
def gradient_over_top(to_fy):
"""The linear gradient just added, turned to fall from the top of the
frame and its middle moved to `to_fy`. A new one is laid across the
middle facing right, its rotate handle below the centre: that handle,
dragged round to the right, turns it a quarter to face up."""
cx, cy = dr.photo(0.5, 0.5)
handles = [(int(h['x'] + h['w'] / 2), int(h['y'] + h['h'] / 2))
for h in dr.matches('id:GradientHandles::drag')]
rx, ry = max(handles, key=lambda p: p[1])
dr.drag(rx, ry, cx + (ry - cy), cy, 25)
pause(1.0)
dr.drag(cx, cy, *dr.photo(0.5, to_fy), 25)
pause(1.2)
@scene(media=['film-local.gif'], sources=MASK_SRC + ['core/dr-film/src/**', 'core/dr-pipeline/src/ops/film_sim.rs'])
def film_local():
"""A negative stock on the whole photograph, then a gradient over the
sky whose Print Exposure burns it in, as a printer would under the
enlarger, the mask drawn as its edge so the print shows through; Done
Masking, then Before held."""
at_develop(ALPS_SKY)
in_column('Film@Button')
film_list_open()
choose_film(FILM_LOCAL)
tool('Local')
rec('film-local')
pause(0.6)
dr.click(*in_column('Linear@Button'))
pause(1.5)
gradient_over_top(0.36)
dr.click(*in_column('Edge@RadioButton')) # an outline, so the tint hides nothing
pause(1.2)
slide('Print Exposure', 28, 30)
pause(2.0)
dr.click_on('Done Masking@Button')
pause(2.0)
hold_before(1.6)
pause(1.0)
cut()
tool('Local') # masks shown tinted again, as the other scenes
dr.click(*in_column('Tint@RadioButton')) # expect; offered while one exists
pause(0.6)
tool('Photo')
undo_all()
FILM_LAST = 'Ilford HP5 Plus' # the last stock `DevelopSession::film_choices` lists
FILM_ROWS = 28 # None and the 27 camera stocks
FILM_ROW, FILM_LIST_MAX = 32, 320