Picture a panorama too large for one texture being navigated
A 22 927 × 8966 Lightroom panorama of a glacier, opened in develop: the wheel in from fit to the peaks, a slow pan along the ridge at that zoom, back out, then 1:1 with a double-click and fit with another. The scene borrows the file from outside the demo library, copies it into a panorama folder, restarts so the scan finds it, and deletes it and the sidecar the app writes beside it before restarting again; it is registered last so no other scene sees it. The film is fine ice and rock in every frame and was 22 MB at the usual 960 px and ten frames a second, so a scene can now name its own GIF size (GIF_SIZE, read by record.sh through `scenes.py gif`); this one is 640 px at eight, 8.7 MB.
This commit is contained in:
@@ -47,6 +47,12 @@ manual does not show; it reads two files and runs in CI. `--changed`
|
||||
re-records a scene when one of its sources, or its own code, has changed
|
||||
since the commit that last touched its pictures.
|
||||
|
||||
GIFs are made at 960 px and ten frames a second. A scene whose film does
|
||||
not compress at that size says otherwise in `GIF_SIZE`, which `record.sh`
|
||||
reads through `scenes.py gif NAME`. `giant_pano` borrows a 500 MB panorama
|
||||
from outside the library (`DR_GIANT_DNG`, by default
|
||||
`/var/tmp/dr-dng/IMG_4181-Pano.dng`) and removes it again, so it runs last.
|
||||
|
||||
Two things the scripts know that are not obvious: a Slint `TouchArea` wants
|
||||
a held press, not xdotool's `click`; and the app is driven on Xvfb rather
|
||||
than the desktop because under XWayland at scale 2 a pointer warp lands at
|
||||
|
||||
@@ -150,7 +150,8 @@ python3 "$scenes" record "$media" "${@:-all}"
|
||||
# GIFs for the page; the MP4s are working files and are not kept.
|
||||
for mp4 in "$media"/*.mp4; do
|
||||
[ -e "$mp4" ] || continue
|
||||
"$here/gif.sh" "$mp4" "${mp4%.mp4}.gif" 960 10
|
||||
read -r width fps < <(python3 "$scenes" gif "$(basename "${mp4%.mp4}")")
|
||||
"$here/gif.sh" "$mp4" "${mp4%.mp4}.gif" "$width" "$fps"
|
||||
rm "$mp4"
|
||||
done
|
||||
find "$media" -name '*.png' -newer "$stamp" -exec optipng -quiet -o2 {} + || true
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
scenes.py changed the scenes whose sources changed since
|
||||
their pictures were last committed
|
||||
scenes.py list every scene, its pictures and sources
|
||||
scenes.py gif NAME the width and frame rate NAME.gif is
|
||||
made at (960 10 unless a scene says)
|
||||
|
||||
Each scene drives the running app through `drive.py` and leaves the
|
||||
pictures it names in OUT_DIR: PNGs, and MP4s that `record.sh` turns into
|
||||
@@ -1527,6 +1529,99 @@ def duplicates():
|
||||
pause(3)
|
||||
|
||||
|
||||
|
||||
# --- a photograph larger than one texture -------------------------------------
|
||||
# Last on purpose: it adds a file to the library for its own length only, and
|
||||
# no scene after it should find that file there.
|
||||
GIANT_DNG = os.environ.get('DR_GIANT_DNG', '/var/tmp/dr-dng/IMG_4181-Pano.dng')
|
||||
GIANT = 'IMG_4181-Pano' # a 22927 × 8966 Lightroom panorama of a glacier at dusk
|
||||
|
||||
# A GIF that is fine rock and ice in every frame does not compress; at the
|
||||
# usual 960 px and ten frames a second this one was 22 MB, and 11 at 720.
|
||||
GIF_SIZE = {'giant-pano': (640, 8)}
|
||||
|
||||
|
||||
def restart_on_library():
|
||||
dr.stop()
|
||||
dr.launch([])
|
||||
dr.wait_ready()
|
||||
pause(5)
|
||||
at_library() # it reopens on the photograph it closed on
|
||||
pause(3)
|
||||
|
||||
|
||||
def remove_giant(folder, made_folder):
|
||||
for f in os.listdir(folder) if os.path.isdir(folder) else []:
|
||||
if f.startswith(f'{GIANT}.'):
|
||||
os.remove(os.path.join(folder, f))
|
||||
if made_folder and os.path.isdir(folder) and not os.listdir(folder):
|
||||
os.rmdir(folder)
|
||||
|
||||
|
||||
@scene(media=['giant-pano.gif'],
|
||||
sources=DEVELOP_SRC + ['ui/dr-ui/src/develop/session.rs', 'ui/dr-ui/src/develop/render.rs',
|
||||
'core/dr-gpu/src/demosaic.rs', 'core/dr-pipeline/src/tiles.rs'])
|
||||
def giant_pano():
|
||||
"""A linear DNG too large for one GPU texture, opened and navigated: in
|
||||
from fit on the peaks at its left as far as the wheel goes (sixteen
|
||||
times fit, short of 1:1 on a frame this wide), a slow pan along the
|
||||
ridge, back out, then 1:1 on the peaks with a double-click and fit with
|
||||
another. The file is not in the demo library (it is 500 MB): it is
|
||||
copied into a `panorama` folder for this scene, the app restarted so the
|
||||
scan catalogues it, and the copy and the sidecar the app writes beside
|
||||
it deleted, and the app restarted again, afterwards. DR_GIANT_DNG names it."""
|
||||
folder = os.path.join(DEMO_LIBRARY, 'panorama')
|
||||
copy = os.path.join(folder, f'{GIANT}.dng')
|
||||
made_folder = not os.path.isdir(folder)
|
||||
try:
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
shutil.copy2(GIANT_DNG, copy)
|
||||
restart_on_library()
|
||||
at_library()
|
||||
grid_top()
|
||||
for _ in range(60): # until the scan has catalogued it
|
||||
if dr.present(f'{GIANT}@ListItem'):
|
||||
break
|
||||
pause(2)
|
||||
dr.click(*cell(GIANT))
|
||||
dr.wait_for(f'{GIANT}.dng', 120)
|
||||
wait_canvas(300)
|
||||
pause(8) # the reduced copy, drawn
|
||||
a = dr.photo(0.14, 0.22) # the snow peaks at the left
|
||||
rec('giant-pano')
|
||||
pause(1.5)
|
||||
for _ in range(10): # the wheel's limit, 16 times fit
|
||||
wheel(-1, *a)
|
||||
pause(0.7)
|
||||
pause(1.5)
|
||||
cx, cy = mid(dr.rect('id:canvas-image'))
|
||||
for _ in range(3): # along the ridge, rightwards
|
||||
dr.drag(cx + 350, cy, cx - 350, cy, 90)
|
||||
pause(1.0)
|
||||
for _ in range(10):
|
||||
wheel(1, cx, cy)
|
||||
pause(0.5)
|
||||
pause(1.5)
|
||||
# 1:1 is past the wheel's limit on a frame this wide: a double-click
|
||||
# reaches it, on the peaks the film started from, and another fits.
|
||||
b = dr.photo(0.2, 0.2)
|
||||
dr.move(*b)
|
||||
pause(0.3)
|
||||
dr.x('click', '--repeat', 2, '--delay', 80, 1)
|
||||
pause(3.0)
|
||||
dr.x('click', '--repeat', 2, '--delay', 80, 1)
|
||||
pause(2.0)
|
||||
cut()
|
||||
finally:
|
||||
# Back to the grid first, or the next start reopens the photograph
|
||||
# it closed on and writes its sidecar again.
|
||||
at_library()
|
||||
dr.stop()
|
||||
remove_giant(folder, made_folder)
|
||||
restart_on_library()
|
||||
remove_giant(folder, made_folder)
|
||||
|
||||
|
||||
# --- running them -----------------------------------------------------------
|
||||
def order(names):
|
||||
"""`names` with their prerequisites, each once, prerequisites first."""
|
||||
@@ -1677,6 +1772,8 @@ def main():
|
||||
elif cmd == 'changed':
|
||||
for name, why in changed():
|
||||
print(f'{name}\t{why}')
|
||||
elif cmd == 'gif':
|
||||
print(*GIF_SIZE.get(rest[0], (960, 10)))
|
||||
elif cmd == 'list':
|
||||
for s in SCENES.values():
|
||||
after = f' (after {", ".join(s.after)})' if s.after else ''
|
||||
|
||||
Reference in New Issue
Block a user