diff --git a/Titano/.vscode/extensions.json b/Titano/.vscode/extensions.json
new file mode 100644
index 0000000..754703a
--- /dev/null
+++ b/Titano/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ // Fornisce il debugger "coreclr" richiesto da launch.json.
+ "ms-dotnettools.csharp",
+ "ms-dotnettools.csdevkit"
+ ]
+}
diff --git a/Titano/.vscode/launch.json b/Titano/.vscode/launch.json
new file mode 100644
index 0000000..dd634c1
--- /dev/null
+++ b/Titano/.vscode/launch.json
@@ -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"
+ }
+ ]
+}
diff --git a/Titano/.vscode/tasks.json b/Titano/.vscode/tasks.json
new file mode 100644
index 0000000..00730fe
--- /dev/null
+++ b/Titano/.vscode/tasks.json
@@ -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"
+ }
+ ]
+}
diff --git a/Titano/Modifiche.txt b/Titano/Modifiche.txt
index 1c506eb..e69de29 100644
--- a/Titano/Modifiche.txt
+++ b/Titano/Modifiche.txt
@@ -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.
diff --git a/Titano/Program.cs b/Titano/Program.cs
index 3bfeb26..128d88e 100644
--- a/Titano/Program.cs
+++ b/Titano/Program.cs
@@ -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;
}
+ ///
+ /// 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.
+ ///
+ private static void EnsureConsole()
+ {
+ if (Console.IsOutputRedirected) return;
+ AttachConsole(AttachParentProcess);
+ }
+
///
/// 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.