• docs/door_graphics_audio_matrix.md src/doors/syncdoom/syncdoom.c src/d

    From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Mon Jul 13 21:37:58 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/d93658bb86ce6e576031a4d9
    Modified Files:
    docs/door_graphics_audio_matrix.md src/doors/syncdoom/syncdoom.c src/doors/syncduke/syncduke.h syncduke_input.c syncduke_io.c src/doors/termgfx/README.md apc.c apc.h caps.h geometry.c geometry.h
    Log Message:
    doors: let the terminal do the 2x upscale (graphics-APC ZX/ZY)

    SyncDuke and SyncDOOM both render 320x200 natively -- doomgeneric pixel- doubles Doom's 320x200 render into its 640x400 framebuffer -- and then
    upscaled the frame door-side before encoding it. CTerm 1.332 added integer ZX/ZY zoom factors to the graphics APC (SourceForge syncterm feature-request #136), so the door can ship the NATIVE frame and let the terminal replicate
    the pixels instead. The picture is identical -- every upscale in that chain
    was already nearest-neighbor -- and a 2x frame costs ~2.4-2.8x the bytes to encode at any quality setting, so this is the ~60% bandwidth reduction the request was filed for, at no cost in quality.

    termgfx_geom_zoom() decides whether the terminal can do it: only when the fitted size is an EXACT integer multiple of the source. SyncTERM has no resampler -- its only scaling anywhere, sixel raster attributes included, is whole-pixel replication -- so any other fit still gets the door-side resample it always had. In practice that means every standard 80-column canvas whose
    fit lands on 640x400 (80x25, 80x50, 80x30/80x60 letterboxed, 132-col letterboxed) gets ZX=2;ZY=2, while an awkward canvas height (640x384 with the status line left on, 640x392, EGA 640x350) falls back. The factor is always
    2x: a 4x fit would need a 1280-wide canvas and SyncTERM's widest is 1056.

    termgfx_apc_image_zoom() emits ZX/ZY among the APC options, and omits them at 1x, so termgfx_apc_image() is now a wrapper around it and the doors NOT
    touched here (SyncConquer) emit byte-identical output -- verified by memcmp, not by eye. Gated on TERMGFX_CTERM_VER_ZOOM (1332): against an older SyncTERM every door emits exactly what it did before.

    The Ctrl-S stats strip grows an "x2" token next to "blob" whenever the
    terminal is actually doing the upscale, because otherwise there is no way to tell from a live session which side performed it. It reports the zoom SENT per frame, not the capability, so it cannot lie about the gate.

    Also documents client-side scaling properly in the door matrix, which never covered it. The sixel raster attribute (pan;pad) is the older mechanism and support fragments three ways: SyncTERM scales BOTH axes (reading pan/pad as integer scales -- a cterm extension), foot/Contour/Windows Terminal honor only the vertical DEC pixel aspect, and xterm/WezTerm render at the encoded size. That is why the doors send pan=2 broadly but pad=2 only to SyncTERM. The SyncDuke comment claiming pan=2 is "a portable DEC 2:1 aspect every sixel terminal honors" was wrong and is corrected; Windows Terminal is added to the termgfx compat matrix.

    Two traps worth knowing, both now written down: keeping the client's status line forfeits the ZX/ZY optimization entirely (640x384 is not a multiple of 200), which is a load-bearing reason for the doors to hide it rather than a cosmetic one; and the full-res sixel tier is a STICKY per-user preference that the F4 cycle persists, so a one-off test cycle silently becomes the next session's starting tier -- and a full-res encode never exercises the pan=2
    path on any client, which is how the vertical-scaling question stayed
    unsettled for so long.

    Builds clean under GCC (zero warnings in the touched files). NOT live-tested: it needs a SyncTERM built with the 1.332 APC zoom support.
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)