Rewrite --render to compute the spectrogram and write a PNG entirely on the
CPU, with no window, no GL context, and no X server. Previously it opened a
hidden GL window and grabbed LoadImageFromScreen(), which still required an X
server (Xvfb); the output was a UI screenshot rather than the spectrogram data.
The new path (RunHeadlessRender) loads the WAV, computes the STFT, colorizes
the bitmap at native STFT resolution, bakes the mLnL annotation overlay onto
it, and exports — all CPU-only. render.c gains a GL-free colorize
(BuildSpectrogramImageCPU), a CPU font loader (LoadFontCPU), and a CPU overlay
drawer (DrawAnnotationsToImage).
Annotations draw outline + label only: mLnL captures contain many overlapping
full-band boxes whose translucent fills alpha-stack to opaque and bury the
signal. The outline marks each region while the spectrogram reads through; a
dark backing strip keeps labels legible. Note: MeasureTextEx/ImageText* bail
when font.texture.id == 0, so the CPU font sets a sentinel non-zero id (the
draw path reads glyph images, never the texture).
Render options: --annotation-opacity (overlay strength), --annotation-kinds
(comma-separated kind filter), --width (resize; default native). Removed the
obsolete --pane/--height window options and the screenshot workaround.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Portable guide for an agent doing refactor/code-health work on a raylib
(or any GLFW/OpenGL) app: Xvfb + screenshot + pixel-diff loop, xdotool
key/mouse injection (incl. the press-release-must-span-frames gotcha),
determinism strategies for static apps vs continuously-animating games,
temp-keybinding state forcing, and a porting checklist for shot_input.sh.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>