From ccad606a43ef0fba5b2b1bd3efaf2ba07bd8c05f Mon Sep 17 00:00:00 2001 From: duffyduck Date: Fri, 24 Jul 2026 01:59:19 +0200 Subject: [PATCH] Titelbild in voller Aufloesung (320x480 anamorph statt halbiert) Stefan-Frage "hatte das Titelbild nicht bessere Aufloesung?" -- ja, bestaetigt. Die 4 Quadranten KELL256A-D (je 160x240 nativ) wurden bisher auf halbe Hoehe (160x120) gestaucht -> 320x240, also die halbe vertikale Aufloesung verworfen (gleiche Fehlklasse wie der Rauser-0.5x-Downscale). Ein RMSE-Vergleich der beiden Varianten gegen die echte DOSBox-Referenzaufnahme belegt eindeutig die volle Variante: voll (320x480, anamorph angezeigt): RMSE 0.014 halbiert (320x240): RMSE 0.083 (6x schlechter) Fix: build_title_quadrants nutzt die nativen 160x240-Quadranten -> 320x480- Bild; angezeigt wird es anamorph auf die volle Fenstergroesse gestreckt (_blit_title: 320x480 -> 960x720, x3 Breite / x1.5 Hoehe), womit alle 480 Zeilen erhalten bleiben. render_title_wipe_frame arbeitet automatisch bei 320x480 (Hoehe aus get_height()). pcc_surface_half_height entfernt (ungenutzt). Verifiziert: neues Titelbild sichtbar schaerfer, deckungsgleich zur Referenz; python -m game --once (headless) exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- game/intro.py | 81 +++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/game/intro.py b/game/intro.py index 54f85e5..49b3e4d 100644 --- a/game/intro.py +++ b/game/intro.py @@ -66,20 +66,17 @@ Serie vermessen, siehe NOTES.md Abschnitt "Boot-Sequenz vermessen". Kurzfassung: 6. kurze schwarze Pause 7. Titelbild "TONY & FRIENDS in Kellogg's Land" -- die 4 Quadranten KELL256A (oben links) / B (oben rechts) / C (unten links) / D (unten rechts) sind - je 160x240 PCC-Nativaufloesung, werden aber nur bei **halber Groesse** - (160x120) in ein 2x2-Raster gezeichnet: A|B oben, C|D unten -> 320x240 - Gesamtbild. Per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme - verifiziert. Reveal-Effekt: obere Haelfte (A|B) waechst von oben (Hoehe - 0->120), untere Haelfte (C|D) waechst von unten (Hoehe 0->120) -- treffen - sich in der Mitte. Waehrend dieser Reveal-Phase ist die untere Haelfte - erst graustichig/entsaettigt MIT Scanlines ueberzogen, die sich danach in - einer zweiten Phase zu vollen Farben aufloesen (Stefans "Screenshot - 4/5/6"-Effekt). HINWEIS: diese Quadranten nutzen denselben - Downscale-Mechanismus (smoothscale halbe Hoehe) wie die Rauser-Assets - vor dem obigen Fix -- hier bisher KEINE Bildschaerfe-Beschwerde von - Stefan, deshalb unangetastet gelassen. Falls das Titelbild auch mal als - "verwaschen" gemeldet wird: gleicher Fix-Ansatz (native Groesse zeichnen, - nur einmal ganzzahlig hochskalieren) anwendbar. + je 160x240 PCC-Nativaufloesung und werden in NATIVER Groesse in ein + 2x2-Raster gezeichnet: A|B oben, C|D unten -> 320x480 Gesamtbild. Das wird + beim Anzeigen ANAMORPH auf 4:3 (Fenstergroesse) gestreckt -- doppelt hohe + Pixel, genau wie im Original (siehe _blit_title). KORREKTUR 24.07.2026: + frueher wurden die Quadranten auf halbe Hoehe gestaucht (320x240) -- das + war falsch (halbe vertikale Aufloesung). RMSE-Vergleich gegen die echte + DOSBox-Referenz: 0.014 (voll) vs. 0.083 (halbiert). Reveal-Effekt: obere + Haelfte (A|B) waechst von oben, untere (C|D) von unten, treffen sich in + der Mitte (y=240). Waehrend/nach dem Reveal ist die untere Haelfte mit + Scanlines ueberzogen (KEIN Grau mehr), die sich von der Mitte nach unten + aufloesen. Alle Nicht-BOB/ICO/MAP-Assets werden per kellogg_formats.load_pcc() (Pillows PCX-Decoder) aus PCKELL.DAT gelesen -- kein eigener RLE-Code mehr (siehe @@ -220,6 +217,17 @@ def _blit_centered(surface): keinen veralteten Screen-Bezug hinterlaesst).''' blit_scaled_centered(pygame.display.get_surface(), surface, WINDOW_W, WINDOW_H) + +def _blit_title(surface): + '''Titelbild-Sonderfall: die Quelle ist 320x480 (ein 4:3-Bild mit doppelt + hohen Pixeln -- genau so liegt es im Original vor, siehe Titelbild- + Aufloesungs-Fund 24.07.2026). Es wird ANAMORPH auf die volle Fenstergroesse + gestreckt (320x480 -> 960x720, also x3 Breite / x1.5 Hoehe) -- fuellt das + Fenster wie die 320x240-Screens, behaelt aber die volle vertikale + Aufloesung (480 statt der frueher faelschlich halbierten 240 Zeilen).''' + s = pygame.display.get_surface() + s.blit(pygame.transform.scale(surface, (WINDOW_W, WINDOW_H)), (0, 0)) + # "praesentiert"-Text-Faelle in KELLOGGS.PCC (siehe Docstring) PRAESENTIERT_FADE_INDICES = [34, 42, 43, 44, 45] PRAESENTIERT_FADE_SECONDS = 2.5 @@ -389,20 +397,6 @@ class Assets: new_size = (max(1, round(w * scale)), max(1, round(h * scale))) return pygame.transform.scale(surf, new_size) - def pcc_surface_half_height(self, name): - '''Skaliert NUR die Hoehe um 0.5, Breite bleibt nativ -- fuer die - Titelbild-Quadranten KELL256A/B/C/D (160x240 -> 160x120). ACHTUNG: - das ist KEINE gleichmaessige Skalierung wie bei den Rauser-Assets -- - per Pixel-Rekonstruktion gegen eine echte DOSBox-Aufnahme verifiziert - (siehe Docstring ganz oben). Nearest-Neighbor (pygame.transform.scale) - statt smoothscale aus dem gleichen Grund wie bei den Rauser-Assets - (siehe pcc_surface_scaled) -- konsistent scharf.''' - surf = self.pcc_surface(name) - w, h = surf.get_size() - new_size = (w, max(1, round(h * 0.5))) - return pygame.transform.scale(surf, new_size) - - def fit_scale(src_w, src_h, max_w, max_h): return min(max_w / src_w, max_h / src_h) @@ -720,20 +714,25 @@ def scene_rauser(clock, assets, audio_paths): # --------------------------------------------------------------------------- def build_title_quadrants(assets): - '''Liefert die 4 Titelbild-Quadranten bei halber Hoehe (160x120 statt - 160x240 nativ) -- A/B oben, C/D unten. Per Pixel-Rekonstruktion gegen - eine echte DOSBox-Aufnahme verifiziert.''' + '''Liefert die 4 Titelbild-Quadranten in NATIVER Aufloesung (je 160x240) + -- A/B oben, C/D unten -> zusammen ein 320x480-Bild. KORREKTUR 24.07.2026: + frueher wurden die Quadranten auf halbe Hoehe (160x120) gestaucht; ein + Vergleich gegen die echte DOSBox-Referenz (RMSE 0.014 voll vs. 0.083 + halbiert) zeigt aber, dass das Original das Bild in voller Hoehe fuehrt und + nur ANAMORPH (doppelt hohe Pixel) auf 4:3 anzeigt. Die native Hoehe bleibt + also erhalten und wird erst beim Anzeigen gestreckt (siehe _blit_title).''' return { - 'A': assets.pcc_surface_half_height('KELL256A'), - 'B': assets.pcc_surface_half_height('KELL256B'), - 'C': assets.pcc_surface_half_height('KELL256C'), - 'D': assets.pcc_surface_half_height('KELL256D'), + 'A': assets.pcc_surface('KELL256A'), + 'B': assets.pcc_surface('KELL256B'), + 'C': assets.pcc_surface('KELL256C'), + 'D': assets.pcc_surface('KELL256D'), } def build_title_image(quads): - '''Komplettes, ruhendes Titelbild (320x240) aus den 4 halbskalierten - Quadranten im 2x2-Raster.''' + '''Komplettes, ruhendes Titelbild (320x480) aus den 4 nativen Quadranten im + 2x2-Raster (wird beim Anzeigen anamorph auf 4:3 gestreckt, siehe + _blit_title).''' a, b, c, d = quads['A'], quads['B'], quads['C'], quads['D'] w = a.get_width() + b.get_width() h = a.get_height() + c.get_height() @@ -775,10 +774,10 @@ def render_title_wipe_frame(quads, t): unteren Haelfte loesen sich von der Mitte nach unten auf. Obere Haelfte ist durchgehend volle Farbe ohne Scanlines (wie in der DOSBox-Referenz).''' a, b, c, d = quads['A'], quads['B'], quads['C'], quads['D'] - half_h = a.get_height() # 120 + half_h = a.get_height() # 240 (native Quadrantenhoehe) full_w = a.get_width() + b.get_width() # 320 - canvas = pygame.Surface((full_w, half_h * 2)) + canvas = pygame.Surface((full_w, half_h * 2)) # 320x480 canvas.fill((0, 0, 0)) if t < TITLE_HEIGHT_REVEAL_FRACTION: @@ -866,12 +865,12 @@ def scene_title(clock, assets, hold_forever=True): kurz und kehrt zurueck, statt auf eine Taste zu warten.''' quads = build_title_quadrants(assets) r = wait_or_skip(clock, TITLE_WIPE_SECONDS, - lambda t: _blit_centered(render_title_wipe_frame(quads, t))) + lambda t: _blit_title(render_title_wipe_frame(quads, t))) if r == 'quit': return 'quit' # 'skip' waehrend der Wipe-Animation -> direkt zum fertigen Titelbild title_img = build_title_image(quads) - draw = lambda _t: _blit_centered(title_img) + draw = lambda _t: _blit_title(title_img) if hold_forever: return wait_for_key(clock, draw) return wait_or_skip(clock, 3.0, draw)