This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/screen-transitions.md
Sylpheed RE agent 7fef19b3a0 re: the transition between screens is a fade through black, and most of
its timing is on the disc

Q7. Every title-side screen carries a full-screen black .prm quad that
paints last, and its keyframe group IS the transition: black at T0, clear
by T1, clear until T2, then back to black on exit. Read with the corpus's
start-of-a-ramp rule and Q1's time unit that gives 0.87s for EXTRAS,
0.97s for the main menu, 4.08s for the title -- from the file, not from a
stopwatch.

The disc-wide check is per-pak all-or-nothing rather than the 41% the
headline count suggests, and GP_TITLE's 6 of 12 is the useful row: the
six builds carrying a fade quad are exactly the six SCREENS, and the six
without are exactly the six overlays. GP_DIALOG is 0 of 133. That is
independent corroboration of the overlay finding from two iterations ago.

One piece is NOT on the disc and says so: the fade-OUT length. The fourth
keyframe has no time slot, because a group's last block stops four bytes
short. Measured instead, at 30fps, ~0.4s and the same both directions.

And a warning I earned: the luminance rise after a transition is NOT the
quad's ramp. The incoming screen's own elements animate in after the quad
has cleared -- 1.47s observed against a declared 0.97s. Time the fade
from where the frame is pure black.

Rig: screenshot samples at 0.5 Hz and cannot see a 0.4s fade at all,
which is why an earlier burst called this an instant cut. ffmpeg x11grab
at 30fps instead; both go in METHOD.
2026-08-28 18:05:19 +00:00

118 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# What happens between two screens — a fade through black, and where its timing lives
**Status:**`CONFIRMED`. The quad and its ramp are **decoded** (a keyframe
group on the disc, with a disc-wide check); the wall-clock timings are
**measured** off the running game at 30 fps. One piece is **undecodable from this
field** and is called out below.
Answers [MISSION Q7](../port/MISSION.md).
## The mechanism — decoded
Every title-side screen carries a full-screen untextured primitive that is
**black, and paints last**: `pteff00.prm` in `GP_TITLE`, `pfeff00.prm` in
`GP_SAVE_LOAD`. That it sorts last was already established
([`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md)); what is
new here is that its **keyframe group is the transition**.
The group is always four blocks, and always this shape:
| block | alpha | meaning |
|---|---|---|
| 1 | `0xff` at `t = T0` | the screen starts **black** |
| 2 | `0x00` at `t = T1` | ramp to fully clear — the screen **fades in** |
| 3 | `0x00` at `t = T2` | clear; this is the resting pose, the quad is invisible |
| 4 | `0xff`, **no time** | ramp back to black — the screen **fades out** on exit |
Read with the corpus's rule that a keyframe is the *start* of a ramp
([`structures/ui-resting-pose.md`](structures/ui-resting-pose.md)).
```
$ tools/re-capture/fade_quads.py 4 5 6 # GP_TITLE
build 4 (title) pteff00.prm t=16 α=255 t=261 α=0 t=269 α=0 t=— α=255
build 5 (main menu) pteff00.prm t=12 α=255 t= 70 α=0 t= 80 α=0 t=— α=255
build 6 (EXTRAS) pteff00.prm t=12 α=255 t= 64 α=0 t= 74 α=0 t=— α=255
```
Under [Q1](ui-keyframe-time-unit.md)'s `1 unit = 1/60 s`: the screen holds black
for **0.20 s**, then fades in over **0.87 s** (`EXTRAS`), **0.97 s** (main menu)
or **4.08 s** (the title).
### ❔ The fade-OUT duration is not in this field
The fourth block has **no time** — a group's last block stops 4 bytes short and
that word is already the next group's element index
([`ui_layout.rs`](../../crates/sylpheed-formats/src/ui_layout.rs) documents the
packing). So the disc gives the ramp's *target* (black) and not its length. That
duration is **measured** below, and the port is authoring it.
### The disc-wide check, and what it shows about overlays
Over every `GP_*.pak`, counting bundles that have a `.prm` element **and** ≥8
elements (screen-sized rather than a two-element fragment):
> **40 of 97** carry a 255 → 0 → 255 quad; 56 of the 60 found disc-wide have
> exactly 4 keyframes.
41 % sounds weak until it is read per pak, where it is nearly all-or-nothing:
| pak | with quad / screen-sized |
|---|---|
| `GP_MISSION_SELECT`, `GP_MOVIE_THEATER`, `GP_OPTIONS`, `GP_TUTORIAL`, `GP_SYSTEM` | 2/2 each |
| `GP_BUNK`, `GP_CHALLENGE` | 6/6 |
| `GP_STAGE_CLEAR` | 4/4 |
| `GP_SAVE_LOAD` | 10/12 |
| **`GP_TITLE`** | **6/12** |
| `GP_DIALOG` | 0/133 |
| `GP_DEBRIEFING_PILOTLOG` | 4/102 |
**`GP_TITLE`'s 6 of 12 is the interesting row, and it is not a gap.** The six
that carry the quad are exactly the six *screen* builds — title, main menu and
`EXTRAS`, English and Japanese. The six that do not are exactly the six
**overlays**: the `PRESS Ⓐ BUTTON` plate and the two `DELTASABER` plates
([`ui-title-build-map.md`](ui-title-build-map.md)). An overlay composited onto a
screen has no transition of its own, so it has no fade quad — which is
independent corroboration that those builds are overlays rather than screens.
`GP_DIALOG`'s 0/133 says the same thing about dialog boxes.
## The timing — measured
Recorded with `ffmpeg -f x11grab -framerate 30` over the game surface, mean
frame luminance per frame; raw data in
[`captures/transitions/transition-luminance.csv`](captures/transitions/transition-luminance.csv),
filmstrip in
[`transition-filmstrip.png`](captures/transitions/transition-filmstrip.png).
| | main menu → `EXTRAS` (Ⓐ) | `EXTRAS` → main menu (Ⓑ) |
|---|---|---|
| press → first visible change | 0.07 s | 0.37 s |
| **fade-out to black** | **0.367 s** | **0.400 s** |
| **pure black** (luminance 0.02) | 0.233 s | 0.167 s |
| luminance rise until settled | 0.567 s | 1.467 s |
The fade-out is the number the file cannot give, and it comes out the same both
ways: **~0.4 s**, i.e. ~24 units under Q1's rule.
The black hold is *consistent with* the file's 12 units (0.20 s) but does not
confirm it — the plateau spans the tail of the outgoing screen's fade-out and the
head of the incoming screen's black, and this measurement cannot separate them.
### ⚠️ The luminance rise is **not** the quad's ramp
The two columns differ by 2.6× where the quad's declared ramps differ by only
1.12× (58 units vs 52). The filmstrip says why: the background reappears *first*
and the labels arrive after it, so what the luminance curve is timing is the
incoming screen's **own element animations**, not the fade quad. Quoting 1.47 s
as "the main menu's fade" would be wrong. The quad's ramp is decoded; the
screen's build-in is a separate, longer thing.
## For the port
* a screen change is: **fade the outgoing screen to black over ~0.4 s**, hold
black briefly, then **fade the incoming screen in over its own declared ramp**
while its elements play their own keyframes;
* the fade-in ramp is **read from the file** (`T1 T0` on the screen's fade
quad);
* the ~0.4 s fade-out and the black hold are **authored from this page** — the
disc does not carry them.