Avvio in debug da VS Code e correzione dell'output a riga di comando

Aggiunta la configurazione .vscode allineata a quella di Mimante ed Encelado:
F5 apre la finestra di Titano, una seconda configurazione entra con i breakpoint
nella verifica del motore (--selftest), che è un punto d'ingresso separato e non
raggiungibile dall'interfaccia. Nei task: build, verifica, cattura interfaccia
e pulisci.

La verifica usciva con codice 0 ma senza stampare nulla quando lanciata con
"dotnet run" o con l'output su file. La causa è AttachConsole: l'eseguibile è
un'applicazione a finestre e senza aggancio non si farebbe leggere da un
terminale, ma se lo standard output è già dirottato l'aggancio lo sostituisce e
i messaggi finiscono nella console invece che nella pipe. Ora l'aggancio avviene
solo quando l'output non è già dirottato. Verificato nei tre casi: eseguibile
diretto, dotnet run e redirezione su file, 55 righe in tutti e tre.

Svuotato Modifiche.txt: il file è dell'utente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 21:51:39 +02:00
co-authored by Claude Opus 5
parent cc1d040ac0
commit 5d8798f1f7
5 changed files with 163 additions and 74 deletions
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
// Fornisce il debugger "coreclr" richiesto da launch.json.
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
]
}
+43
View File
@@ -0,0 +1,43 @@
{
// Titano è un'applicazione desktop: F5 apre la stessa finestra che si ottiene
// facendo doppio clic su Titano.exe. La seconda configurazione esiste perché la
// verifica del motore NON è raggiungibile da quella finestra — è un punto
// d'ingresso a sé (--selftest), e volerci mettere un breakpoint dentro è la
// ragione più frequente per aprire il debugger su questo progetto.
"version": "0.2.0",
"configurations": [
{
"name": "Titano",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0-windows/Titano.exe",
"args": [],
"cwd": "${workspaceFolder}/bin/Debug/net10.0-windows",
"console": "internalConsole",
"stopAtEntry": false,
"enableStepFiltering": true
},
{
// Terminale integrato e non console interna: --selftest scrive con
// AttachConsole sulla console del processo padre, che nella console interna
// non esiste. Nel terminale l'esito delle 23 verifiche si legge davvero.
"name": "Titano — verifica del motore",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net10.0-windows/Titano.exe",
"args": ["--selftest"],
"cwd": "${workspaceFolder}/bin/Debug/net10.0-windows",
"console": "integratedTerminal",
"stopAtEntry": false,
"enableStepFiltering": true
},
{
"name": "Attach a Titano (processo già avviato)",
"type": "coreclr",
"request": "attach",
"processName": "Titano.exe"
}
]
}
+95
View File
@@ -0,0 +1,95 @@
{
// Stessa impostazione di Mimante ed Encelado: attività "process" che chiamano
// dotnet, nomi in italiano, $msCompile per portare gli errori nel pannello
// Problemi. Titano non ha una catena MSBuild dedicata perché non ne ha bisogno:
// è un solo progetto, senza pacchetti da ripristinare e senza installatore.
"version": "2.0.0",
"tasks": [
{
"label": "build",
"detail": "Compilazione di debug, per F5 e per il controllo rapido degli errori.",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/Titano.slnx",
"-c",
"Debug",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": { "kind": "build", "isDefault": true },
"problemMatcher": "$msCompile"
},
{
"label": "verifica",
"detail": "Genera una sequenza sintetica e la fa attraversare tutta la pipeline: 23 controlli, encoder e contenitore compresi. Da eseguire dopo ogni modifica.",
// In Release e non in Debug: le stesse verifiche passano da ~47 s a ~6 s,
// perché i cicli in virgola mobile di flow e blur non sono ottimizzati in Debug.
"type": "process",
"command": "dotnet",
"args": [
"run",
"--project",
"${workspaceFolder}/Titano.csproj",
"-c",
"Release",
"--",
"--selftest"
],
"group": { "kind": "test", "isDefault": true },
"presentation": { "reveal": "always", "panel": "dedicated" },
"problemMatcher": "$msCompile"
},
{
"label": "cattura interfaccia",
"detail": "Salva un'immagine della finestra in bin/interfaccia.png. Serve a controllare la resa dei controlli disegnati a mano senza doverli guardare a occhio.",
"type": "process",
"command": "dotnet",
"args": [
"run",
"--project",
"${workspaceFolder}/Titano.csproj",
"-c",
"Release",
"--",
"--capture",
"${workspaceFolder}/bin/interfaccia.png",
"${input:sequenza}",
"${input:scheda}"
],
"presentation": { "reveal": "always", "panel": "dedicated" },
"problemMatcher": []
},
{
"label": "pulisci",
"detail": "Rimuove bin e obj di entrambe le configurazioni.",
"type": "process",
"command": "dotnet",
"args": ["clean", "${workspaceFolder}/Titano.slnx", "-v", "m"],
"problemMatcher": []
}
],
"inputs": [
{
"id": "sequenza",
"type": "promptString",
// Il valore predefinito è la sequenza lasciata sul disco dal task "verifica":
// eseguito quello, la cattura ha già qualcosa di sensato da mostrare.
// Lasciando il campo vuoto si cattura la finestra senza sequenza caricata.
"description": "Cartella della sequenza da caricare (vuoto = finestra vuota)",
"default": "${env:TEMP}\\Titano.SelfTest\\sequenza"
},
{
"id": "scheda",
"type": "pickString",
"description": "Quale sezione del pannello di configurazione mostrare",
"options": [
{ "label": "Generale", "value": "0" },
{ "label": "Elaborazione immagini", "value": "1" },
{ "label": "Esportazione", "value": "2" }
],
"default": "0"
}
]
}
-72
View File
@@ -1,72 +0,0 @@
TITANO - registro delle modifiche
=================================
2026-08-13 Prima versione completa dell'applicazione.
Sviluppo da zero di Titano, applicazione desktop per time-lapse professionali,
con il vincolo di non usare alcuna libreria o strumento di terze parti.
Il progetto non contiene nessun PackageReference: oltre alla libreria standard
di .NET si usano solo API native di Windows richiamate via P/Invoke scritto a mano.
MODULI REALIZZATI
1. Ingestion e parsing metadati (Metadata/)
- Parser binario TIFF/Exif proprietario: header, catena di IFD, tutti i tipi
di dato della specifica, sotto-directory Exif e GPS.
- Riconoscimento dei contenitori: JPEG (percorso dei marker senza decodifica),
TIFF e RAW derivati, PNG (percorso dei chunk), HEIF/AVIF/WebP per scansione.
- Scanner XMP proprietario come sorgente complementare.
- Timestamp con frazione di secondo (SubSecTimeOriginal) e fuso orario;
tempo di posa, apertura e ISO anche nelle varianti APEX.
- Calcolo degli intervalli reali, cadenza nominale mediana e segnalazione
delle pause dell'intervallometro.
2. Deflicker e smoothing dell'esposizione (Analysis/)
- Misura della luminanza come media logaritmica troncata su istogramma,
calcolata in luce lineare su campionamento a griglia fissa.
- Curva target da regressione lineare locale pesata su finestra mobile, con
seconda passata robusta (peso di Tukey) che scarta i fotogrammi anomali.
Le rampe reali di luce restano intatte, lo sfarfallio viene rimosso.
- Applicazione dei guadagni con compressione dolce delle alte luci e
stabilizzazione opzionale del bilanciamento colore.
3. Motion blur sintetico e interpolazione (Motion/)
- Optical flow proprietario: piramide gaussiana e schema differenziale
iterativo su griglia rada, con filtro mediano fra i livelli.
- Shutter angle reale per fotogramma; sfocatura mancante composta in
quadratura e resa con filtro di ricostruzione direzionale.
- Interpolazione temporale con warping bidirezionale per uniformare la
cadenza e gestire in modo adattivo la durata dei fotogrammi.
4. Streaming ed encoding video (Video/)
- Multiplexer MP4 (ISO-BMFF) scritto da zero: ftyp, mdat in streaming con
dimensione a 64 bit, moov completo con stts a durate variabili, stss,
stsz, co64, avcC/hvcC costruiti dai parameter set del bitstream.
- Encoder pilotato direttamente come Media Foundation Transform, con
percorso asincrono per le trasformazioni hardware (Intel/AMD/NVIDIA)
e ripiego automatico su quelle software.
- Conversione RGB lineare a NV12 BT.709 scritta in-house.
5. Interfaccia (UI/)
- Tema scuro con ogni controllo disegnato a mano in GDI+: pulsanti,
interruttori, cursori, schede, barra di avanzamento.
- Grafico vettoriale della curva di esposizione: luminanza misurata
sovrapposta alla curva target, area di correzione, corsia dei guadagni,
fasce delle anomalie di cadenza, zoom, spostamento e tooltip.
- Tabella dei fotogrammi a rendering virtuale.
- Pannello di configurazione in tre sezioni: Generale, Elaborazione
immagini, Esportazione video.
- Anteprima elaborata dallo stesso motore usato in esportazione.
ARCHITETTURA
- Nessun file temporaneo: i dati passano fra le fasi solo in memoria, l'unica
scrittura su disco è il flusso compresso finale.
- Impronta di memoria costante: buffer poolati e canale a capacità limitata,
l'occupazione non dipende dal numero di fotogrammi della sequenza.
- Thread di calcolo separati dal thread dell'interfaccia.
VERIFICA
Comando "Titano.exe --selftest": genera una sequenza sintetica dalle
proprietà note e la fa attraversare l'intera pipeline confrontando 23
grandezze con i valori attesi. Tutte superate, compresa la ri-decodifica
del file prodotto con il lettore di sistema.
+18 -2
View File
@@ -15,7 +15,7 @@ internal static class Program
{
if (args.Length > 0 && args[0] is "--selftest" or "-t")
{
AttachConsole(AttachParentProcess);
EnsureConsole();
string directory = args.Length > 1
? args[1]
: Path.Combine(Path.GetTempPath(), "Titano.SelfTest");
@@ -28,7 +28,7 @@ internal static class Program
if (args.Length > 1 && args[0] == "--capture")
{
AttachConsole(AttachParentProcess);
EnsureConsole();
ApplicationConfiguration.Initialize();
return Capture(args[1], args.Length > 2 ? args[2] : null,
args.Length > 3 && int.TryParse(args[3], out int tab) ? tab : 0);
@@ -39,6 +39,22 @@ internal static class Program
return 0;
}
/// <summary>
/// Rende visibili i messaggi delle modalità a riga di comando.
///
/// L'eseguibile è compilato come applicazione a finestre e quindi nasce senza console:
/// agganciare quella del processo padre è l'unico modo di farsi leggere da un terminale.
/// Ma se lo standard output è già dirottato — una pipe, un file di registro, il task di
/// un editor — l'aggancio sostituirebbe quel dirottamento e i messaggi finirebbero nella
/// console invece che dove sono attesi. In quel caso non si aggancia niente: i gestori
/// ereditati vanno già bene.
/// </summary>
private static void EnsureConsole()
{
if (Console.IsOutputRedirected) return;
AttachConsole(AttachParentProcess);
}
/// <summary>
/// Apre l'interfaccia, opzionalmente vi carica una sequenza, e ne salva un'immagine.
/// Serve a verificare la resa dei controlli disegnati a mano senza intervento manuale.