Zwischen Januar 2022 und März 2026 hat Ansel 297 Non-Merge-Commits eingebracht, die das Leuchttisch-Raster, seine Vorschaubilder sowie deren Rendering-Pipeline und Caching betreffen. Ich habe versucht, so lange wie möglich mit dem schludrigen Leuchttisch-Code von Darktable auszukommen, nur entfettet, aber leider war das reine technische Schulden und es war quälend langsam.
Tatsächlich „bewältigt" Darktable die Miserabilität seines Leuchttisches, indem es dessen Größe reduziert: Die linke und rechte Seitenleiste beanspruchen viel Anzeigefläche, wodurch dem Leuchttisch noch weniger Fläche zum Neuzeichnen bleibt. Da Ansel die rechte Seitenleiste entfernt und ihren Inhalt mit der linken und dem globalen Menü zusammengeführt hat, gab es mehr Fläche zu zeichnen, mehr CPU-Arbeit zu leisten, und das furchtbare Design des Leuchttisches wurde umso schädlicher.
Dieses Projekt ist nun abgeschlossen. Dieser Artikel ist eine Landkarte dessen, was sich verändert hat, was entfernt wurde, was neu gestaltet wurde, wie das neue Verhalten funktioniert und was die Benutzer tatsächlich davon haben.
Note
Etwas Anwendungs-Jargon, der weiter unten verwendet wird, für Leser, die nicht im Quellcode leben:
- Leuchttisch: Ansels Ansicht zum Durchsehen, in der Sie viele Bilder gleichzeitig verwalten, sortieren, auswählen und begutachten.
- Dunkelkammer: Ansels Bearbeitungsansicht, in der Sie ein einzelnes Bild im Detail bearbeiten.
- Filmstreifen: der horizontale Streifen aus Vorschaubildern, der unterhalb oder neben einer anderen Ansicht angezeigt wird und zur schnellen Navigation durch die aktuelle Sammlung dient.
- Vorschaubild: das kleine Vorschaubild, das im Leuchttisch oder Filmstreifen angezeigt wird, zusammen mit Abzeichen, Overlays und Metadaten.
- Thumbtable: das interne Widget, das ein ganzes Raster oder einen ganzen Streifen von Vorschaubildern verwaltet, einschließlich Scrollen, Auswahl, Neuzeichnen und Lebensdauer der Vorschaubilder.
- Overlay: die zusätzlichen Informationen, die über ein Vorschaubild gezeichnet werden, etwa Sterne, Ablehnungsmarkierungen, Farbetiketten, Gruppenränder oder Metadaten.
- Sammlung: die aktuelle Menge an Bildern, die der Leuchttisch nach Anwendung der aktiven Filter, der Suche und der Gruppierungsregeln anzeigt.
- Gruppierte Bilder: mehrere miteinander verknüpfte Bilder, sodass sie als ein einziger zusammengeklappter Stapel oder als aufgeklappte Mitglieder derselben Gruppe angezeigt werden können.
- Sammlungsfilter: das Leuchttisch-Werkzeug, das die aktuelle Sammlung nach Text, Bewertung, Farbetiketten oder anderen Kriterien einschränkt. In Ansel hat dies auch das aufgenommen, was früher Aussortier-Modus (culling mode) genannt wurde.
- Mipmap-Cache: das Subsystem, das vorberechnete Vorschaubilder in mehreren Größen speichert und abruft, sodass Ansel die Quelldatei nicht jedes Mal von Grund auf dekodieren und rendern muss, wenn ein Vorschaubild angezeigt wird.
- History-Hash: ein interner Fingerabdruck des Bearbeitungsverlaufs eines Bildes, der dazu dient zu erkennen, ob ein zwischengespeichertes Vorschaubild noch dem aktuellen Bearbeitungsstand entspricht.
Eine kurze Geschichte des schlechten Designs
Um zu verstehen, warum die Neuentwicklung von 2022-2026 am Ende so viele Dateien berührte, hilft es, einen Schritt weiter zurückzublicken.
Die Leuchttisch-Ansicht, src/views/lighttable.c, hatte über mehr als ein Jahrzehnt hinweg Zuständigkeiten angehäuft. Sie begann als der Ort, der das Dateimanager-Raster und grundlegendes Zoomen handhabte, und absorbierte dann nach und nach die vollständige Vorschau, die angeheftete Vorschau, das Aussortieren (culling), das Merken von Panel-Zuständen, die Gruppierung, das Umsortieren per Drag-and-drop, die Tastaturnavigation, die Overlay-Richtlinien, die Filmstreifen-Koordination und eine Menge Shortcut-Routing. Wenn Sie sich den Verlauf vor 2022 ansehen, geht es bei vielen Commits nur darum, diese daran zu hindern, sich gegenseitig in die Quere zu kommen: Offset-Sprünge beim Zoomen, Fehler beim Vorschau-Scrollen, eine Auswahl, die nicht synchron bleibt, Aussortieren und Vorschau, die in einem halbkaputten Zustand erneut betreten werden, eine Panel-Sichtbarkeit, die nicht ordnungsgemäß wiederhergestellt wird, Gruppenränder mit Grafikfehlern und Bewertungen oder Farbetiketten, die das Raster unerwartet verschieben.
- 2015: den gesamten Leuchttisch neu zeichnen, weil einige Vorschaubilder sich nicht richtig aktualisierten … (man beachte auch den Cairo-Zeichencode, der Gruppenränder direkt vor SQLite-Abfragen und dem Abrufen von Vorschaubildern aus dem Cache zeichnet… zum Sonderpreis von 3 zum Preis von 1).
- … später rückgängig gemacht, weil es die CPU auslaugte (kein Scherz…),
- 2020, Refactoring: … irgendwann wieder als Timeout-Funktion gemacht , deren Erfolg nicht mehr garantiert ist (warum 250 ms?), und im Februar 2020 entfernt,
- … erneut gemacht und im April 2020 wieder entfernt ,
- … weil es in das Vorschaubild verschoben wurde ,
- … im Oktober 2020 gehärtet , sprich einfach so verwendet, wie ein
g_timeoutverwendet werden sollte, weil es ein entferntes Vorschaubild aufrufen und einen Segmentation Fault (dann Absturz) verursachen konnte, - … bis ich es im Februar 2025 endgültig losgeworden bin , um das zu handhaben, indem man… Sie wissen schon, den Vorschaubild-Zeiger direkt dem Hintergrund-Job zuführt, der das Bild erstellen soll, und ihn das gezielte Neuzeichnen aufrufen lässt, wenn es fertig ist (und den Fall elegant behandelt, dass das Vorschaubild entfernt wurde, bevor die Pipeline abgeschlossen war).
Das zeigt also eine völlige Unfähigkeit, den Lebenszyklus eines zwischengespeicherten Bildes im Blick zu behalten (oder überhaupt irgendwelcher Daten in dieser gottverlassenen Software), bei gleichzeitig völliger Gelassenheit und Entspanntheit damit. Ich habe dieses Problem angegangen, indem ich die Hintergrund-Asynchronität vereinfacht habe, aber eine andere Möglichkeit wäre gewesen, ein Signal zu verwenden. Ich komme nicht darüber hinweg, dass die 3 Typen, die 10 Jahre lang daran gearbeitet haben, doppelt so lange C-Code geschrieben haben wie ich. 2018 wusste ich nicht einmal, was GLib war.
Falls es Sie interessiert, so bin ich es angegangen:
- Vorschaubild-Widgets halten eine Zustandsvariable, die die Gültigkeit ihres eigenen Bildes darstellt,
- wenn GTK das Vorschaubild neu zeichnen will und die Zustandsvariable „ungültig" besagt, startet das Vorschaubild-Widget einen Hintergrund-Thread, der eine Pipeline zur Erstellung des fehlenden Bildes berechnet,
- wenn der Hintergrund-Thread zurückkehrt, legt er das Ausgabebild per Zeiger in das Vorschaubild, das ihn aufgerufen hat, in eine (zwischengespeicherte) Oberfläche, aktualisiert die Zustandsvariable auf „gültig" und sendet dann ein GTK-Queue-Redraw-Ereignis an das Widget (nicht an das ganze Raster),
- wenn das Widget unsichtbar ist, passiert nichts (GTK reiht keine Redraw-Ereignisse für unsichtbare Widgets ein). Beim nächsten Mal, wenn GTK das Bild neu zeichnen will, verwendet es die zwischengespeicherte Oberfläche aus dem Widget,
- Verlaufs- und Metadatenänderungen senden ein Signal „image info changed", das die Bild-ID veröffentlicht. Das Thumbtable hat einen Signal-Handler, der darauf hört, der das Vorschaubild anhand der ID im Raster findet (aus einer Hashtabelle, also
O(N)) und seine Zustandsvariable auf „ungültig" zurücksetzt. Auch hier gilt: Wenn 100 Bilder von der Änderung betroffen sind und alle unsichtbar sind, passiert nichts.
Die Architektur wirkt komplexer, aber sie ist tatsächlich weniger Code und sie ist schlicht robust: Es wird nichts geraten.
Eine Neuentwicklung aus der COVID-Ära versuchte, das in src/dtgtk/thumbtable.c umzustrukturieren, was bald Teil derselben Abdrift wurde. Sobald die dedizierte Thumbtable-Abstraktion auftauchte, wurde sie zum Verkehrsregler zwischen Sammlungszustand, Scrollen, Offsets, aktiven Bildern, Tastaturnavigation, Drag-and-drop, sanftem Scrollen, Overlay-Sichtbarkeit, zoombarem Layout, Aussortier-Synchronisation und Filmstreifen-Synchronisation und erfand viele native GTK-Funktionen auf schlechtere und unvollständige Weise neu (die typische Darktable-Manier). Der Verlauf vor 2022 ist voller Commits, die Mouse-over nach dem Scrollen reparieren, Zeilen und Offsets neu berechnen, ausgewählte Bilder nach Sammlungsänderungen sichtbar halten, quadratische Verzögerung bei Home/End vermeiden, zoombare Ausrichtung reparieren, dafür sorgen, dass ausgeblendete oder zusammengeklappte Bilder die Aktiv-Bild-Logik nicht zerstören, und die Anzahl der ausgesendeten Signale begrenzen, weil zu viele unabhängige Aktualisierungen aneinandergekettet wurden. Mit anderen Worten, thumbtable.c war bereits der Ort geworden, an dem View-Logik, Vorschaubild-Lebensdauer und Sammlungsnavigation zwangsweise aufeinandertreffen mussten, lange bevor die Neuentwicklung von 2022 begann.
Das Thumbtable-Refactoring ließ eine neue Datei entstehen, als der alte Leuchttisch- und Filmstreifen-Code begann, in ein gemeinsames Vorschaubild-Widget und ein Thumbtable als Basisobjekt all dessen ausgelagert zu werden: src/dtgtk/thumbnail.c. Das war im Prinzip die richtige Richtung: Statt Vorschaubilder an mehreren Stellen separat zu zeichnen, konnte ein Widget Aktivierung, Auswahl, Overlays, Sterne, Ablehnungsmarkierungen, Drag-and-drop, Gruppenränder und zoombares Verhalten zentralisieren. Aber es folgte sehr schnell einem ähnlichen Weg: Der Verlauf vor 2022 zeigt, wie schnell dieses gemeinsame Widget überladen wurde. Es hatte bald mit erweiterten Overlays, MIPMAP-aktualisierten Callbacks, Aussortier-Verknüpfungen, Scrollbalken, Aktiv-Bild-Handling, Filmstreifen-Callbacks, Vollvorschau-Navigation und CSS-abgestimmter Icon-Platzierung zu tun. Mit anderen Worten, thumbnail.c zeichnete nicht bloß ein Vorschaubild. Es wurde zur gemeinsamen Bruchlinie zwischen Leuchttisch, Filmstreifen, Aussortieren und Vorschau.
Der Filmstreifen begann als dedizierter Navigationsstreifen, gewann dann die Zentrierung auf das aktive Bild, Drag-and-drop, Karten-Interaktionen, Kopier-/Einfüge-Operationen, sanftes Scrollen, HiDPI-Korrekturen, CSS-Anpassung und schließlich Teile des neuen Vorschaubild-Callback-Systems hinzu. Auch hier ist keine dieser Funktionen für sich genommen unvernünftig. Das Problem ist, dass der Filmstreifen am Ende gerade genug eigenes Verhalten hatte, um vom Leuchttisch abzuweichen, während er weiterhin versuchte, einen Teil derselben Vorschaubild-Maschinerie wiederzuverwenden.
Hinzu kam das inkonsistente „Auswahl"-Paradigma in Darktable, verkauft als „klickloser Workflow", sodass viele Schreiboperationen ausgeführt werden konnten, ohne das oder die betroffenen Bilder explizit auszuwählen. Das führte zu vielen ungewollten Effekten und Unfällen, die Datenverluste zur Folge hatten (dem falschen Bild die falsche Sternebewertung zuzuweisen, was es aus der aktuellen Sammlung verschwinden ließ, wenn Sie nach Bewertung filterten) und mühsame Rückgängig-Sitzungen. Aber die gesamte Heuristik der Bildauswahl war ebenfalls fragil: Das Bewegen der Maus im Fenster konnte den Fokus von dem Bild stehlen, das Sie explizit mit einem Klick oder einer Tastaturauswahl gesperrt hatten, aber nicht immer.
Ganz zu schweigen davon, dass dieses „auf Maus-Hover reagieren" mehrere SQL-Abfragen in die Bibliotheksdatenbank auslöste, wenn ein neues Bild überfahren wurde, um aktualisierte Bild-Metadaten zu erhalten und den Inhalt der Metadaten-Module (Metadaten, EXIF- & IPTC-Metadaten, Schlagwörter) zu aktualisieren. Das liegt alles daran, dass sie nicht zwischengespeichert werden konnten, und sie konnten nicht zwischengespeichert werden, weil Darktable nicht in der Lage ist, den Lebenszyklus seiner Daten zu verfolgen, sodass es ständig alles aktualisieren muss. Und es wird dabei Ihren Akku auslaugen.
Als die Neuentwicklung also begann, nachdem Ansel 2022 abgespalten worden war, bestand das Problem nicht darin, dass ein einzelner schlechter Commit den Leuchttisch kaputt gemacht hatte. Das Problem war, dass drei Geschichtsschichten übereinander gestapelt waren:
lighttable.cschleppte noch den Ballast mehrerer (nutzloser) Durchseh-Modi mit, die dort als nachträgliche Einfälle codiert waren,thumbtable.ckonzentrierte Scroll-, Offset-, Aktiv-Bild- und Sammlungsnavigations-Logik, die sich teilweise mit dem Sammlungs- und Auswahl-Backend überschnitt,thumbnail.ckonzentrierte immer mehr gemeinsames GUI-Verhalten plus SQL-Code.
Deshalb musste die spätere Arbeit architektonisch sein. An diesem Punkt gab es keinen realistischen Weg mehr, die Symptome eines nach dem anderen weiter zu beheben.
Änderungen am Frontend
Das Aussortieren war (konzeptionell) nie wirklich eine eigene Ansicht
Ein wichtiger Teil dieser Subtraktion war der Aussortier-Modus (culling mode) . Im Februar 2023 wurden die dedizierten Aussortier- und Vorschau-Ansichten entfernt, weil sie schlimm mit Shortcuts, Ansichtswechseln und Sonderfall-Verzweigungen verflochten waren. Der Code war verstrickt und umständlich, aber das eigentliche Problem lag im Design selbst: Es war eine überentwickelte Lösung für ein viel einfacheres Problem, gelöst in der falschen Schicht.
Der Bedarf bestand darin, eine beliebige Menge von Bildern zu isolieren, die in der aktuellen Sammlung nicht notwendigerweise zusammenhängend waren, um zu entscheiden, welches der Behaltenswerte sein würde. Das erforderte kein neues Layout (oder zwei…); es erforderte einen Filter, um die Sammlung auf eine beliebige Auswahl einzuschränken. Wir hatten bereits Filter, um Sammlungen nach Bewertung, Farbetikett, bearbeitetem/unbearbeitetem Status usw. einzuschränken.
Dieser Unterschied ist wichtig. Eine dedizierte Aussortier-Ansicht dupliziert Probleme, die der Leuchttisch ohnehin lösen muss: welche Bildmenge aktiv ist, wie die Auswahl beim Verlassen der Aussortier-Ansicht wiederhergestellt wird, wie Shortcuts geroutet werden, wie Zoom- und Vorschaubild-Zustand initialisiert werden und was passiert, wenn Sie zum Raster zurückkehren. Jede Verbesserung muss dann zweimal implementiert werden, einmal in der Leuchttisch-Logik und einmal in der Aussortier-Logik, und beide Versionen driften auseinander. Ganz zu schweigen davon, dass der Aussortier-Modus einen statischen und einen dynamischen Modus hatte, die sich sowohl in der Art, wie man mit ihnen interagierte, als auch in ihrer Implementierung unterschieden, und nur sehr wenige Benutzer verstanden, worum es dabei ging.
In Ansel kam das Aussortieren als Sammlungsfilter zurück, zunächst als einfache Möglichkeit, die aktuelle Menge einzugrenzen, später dann explizit als Filter-Schaltfläche Auf Auswahl beschränken. Das bedeutet, dass die für den Benutzer sichtbare Absicht dieselbe blieb (Reduzierung der aktuellen Sammlung auf eine beliebige Auswahl von Bildern), aber die Implementierung sich vollständig änderte: Statt eine spezielle Ansicht zu betreten, bleiben Sie im Leuchttisch und weisen das Filterwerkzeug an, nur die aktuell ausgewählten Bilder oder nur die Bilder anzuzeigen, die dem aktuellen Eingrenzungskriterium entsprechen.

Für die Benutzer ist der Nutzen praktisch. Das Aussortieren ist jetzt mit dem Rest der Filterlogik kombinierbar, statt außerhalb davon zu existieren. Sie können es mit Bewertungen, Farbetiketten oder Textsuche kombinieren, weil es nur ein weiterer Sammlungsfilter ist. Die Shortcut-Behandlung ist einfacher, weil es eine spezielle Ansicht weniger gibt, die um die Hoheit über die Shortcuts konkurriert. Und Korrekturen am Scrollen, an der Auswahl, an der Vorschaubild-Lebensdauer oder am Overlay-Verhalten kommen automatisch auch dem Aussortieren zugute, weil das Aussortieren jetzt dieselbe Leuchttisch-Infrastruktur nutzt statt einer parallelen.
Das war auch eine Gelegenheit, die Filter-Symbolleiste zu überarbeiten, die Schaltflächen und Logik vermischte: eine Combobox-Liste für Bewertungen (von abgelehnt bis 5 Sterne), verbunden mit einer Vergleichs-Combobox-Liste (≠ = > < >= <=), aber Umschalt-Schaltflächen für Farbetiketten. Darktable 4.0 ersetzte das durch einen überentwickelten Satz konfigurierbarer Widgets mit nicht standardisierten GUI-Paradigmen, die in Tooltips erklärt werden mussten. Ansel hat das Design verflacht: Alles ist eine Umschalt-Schaltfläche, die innerhalb ihrer Gruppe in einem „Einschließen"-Modus arbeitet und komplexe Auswahlen ohne komplexe GUI erlaubt:
- Sie haben 3 Gruppen (Bewertungen, Farbetiketten, Bearbeitungsstatus), plus Auf Auswahl beschränken und Textsuche,
- zwischen diesen Gruppen sind die Filter exklusiv, was bedeutet, dass sie ein logisches
ANDsind, - innerhalb dieser Gruppen sind die Filter additiv, was bedeutet, dass sie ein logisches
ORsind.
Zum Beispiel ist im Screenshot oben alles eingeschaltet, sodass die Filterung praktisch deaktiviert ist (wir lassen alles durch). Im Beispiel unten filtern wir alle Bilder ein, die ein Farbetikett gesetzt haben und bereits bearbeitet wurden, unabhängig von ihrer Bewertung (alle Bewertungs-Schaltflächen sind eingeschaltet):

Auswahl: das Vermischen von Backend- und GUI-Zuständen fällt einem immer auf die Füße
Gruppierte Bilder waren eine weitere versteckte Bruchlinie. Ein Teil der alten Logik versuchte, die Gruppierung in SQL-Abfragen zu berücksichtigen, wenn Sammlungen von Bildern aus der Bibliotheksdatenbank generiert wurden, aber Gruppenränder, Hover-Zustände, Auswahlverhalten und die tatsächliche Sichtbarkeit sind GUI-Konzepte. Diese Diskrepanz erzeugte subtile Fehler: Alle Bilder einer Gruppe konnten ausgewählt werden, oder einige wurden gemäß heuristischer Regeln ignoriert, die nicht dem entsprachen, was der Benutzer auf dem Bildschirm sah.
Ich habe das behoben, indem ich Gruppen-Anzeige-Entscheidungen wieder in den GUI-Code verlagert habe. SQL liefert die Liste der Bilder; die Oberfläche entscheidet, welche gruppierten Mitglieder sichtbar, zusammengeklappt oder überfahren sind. Danach wurden die 2025 hinzugefügten Funktionen viel einfacher: Gruppenränder konnten neu implementiert, optional gemacht, auf den Filmstreifen erweitert und beim Überfahren sichtbarer gemacht werden. Tooltips konnten träge (lazy) nur dann befüllt werden, wenn der Benutzer sie überfährt. Auch eine SQL-Anfrage pro Vorschaubild zur Init-Zeit konnte entfallen, weil die Oberfläche bereits genug lokalen Zustand hatte, um zu wissen, wann Gruppeninformationen tatsächlich benötigt wurden.
Für die Benutzer ist der Nutzen nicht abstrakt. Gruppenränder bedeuten jetzt das, was sie anzeigen, Auswahlen entsprechen genau dem sichtbaren Gruppenzustand, und gruppierte Bilder fühlen sich nicht mehr so an, als würden sie halb von der Datenbank und halb von der Oberfläche verwaltet.
Ein weiteres verstecktes Problem war die Auswahl. Im alten Code war die Auswahl zu sehr mit Sammlungs-Interna, rohem SQL-Zugriff und Fallback-Logik verstrickt. Das klingt harmlos, bis ein Bild aus der aktuellen Sammlung entfernt wird, eine Gruppe zusammengeklappt wird oder ein Ansichtswechsel geschieht, während ein anderer Teil des Codes noch vom vorherigen Auswahlmodell ausgeht. In Darktable wurden Bildauswahlen als SQL-Backend behandelt, da sie in der Bibliothek gespeichert werden, um in der nächsten Sitzung wiederhergestellt zu werden. Das ist kein Problem, bis das Auswahl-Backend manipuliert wird, um zu erraten, welche Bilder in der GUI sichtbar sind, und die GUI die Auswahl tatsächlich über das Backend handhabt. Das ist die schlechtestmögliche Aufteilung der Funktionalität zwischen Backend- und GUI-Schichten.
Deshalb enthält 2025 ein ganzes Bündel auswahlbezogener Commits: Neuschreiben der Auswahl-API, Entfernen der alten main_image-Vorstellung, Vereinheitlichung der Aktiv-Bild-Getter/Setter, Beheben der Bereichsauswahl, Wiederherstellen des Auswahlzustands über Ansichtswechsel hinweg und Hinzufügen von rowid-Fallbacks, wenn scroll_to_selected die ursprüngliche Bild-ID nicht mehr finden kann. Die Neuentwicklung änderte die Architektur: Das Thumbtable berechnet jetzt sichtbare, für den Benutzer bedeutungsvolle Auswahlen und übergibt eine Liste von Bild-IDs an die Auswahlschicht, statt dass die GUI versucht, im Nachhinein die Bedeutung aus dem Sammlungszustand zurückzuentwickeln. Die Auswahl-SQL-API ist zu einem einfachen „In Datenbank speichern / aus Datenbank wiederherstellen"-Backend geworden.
Der konkrete Nutzen für den Benutzer ist, dass die Bereichsauswahl mit Shift + Klick und das „Bild, auf das gewirkt werden soll" What You See Is What You Get sind: Sie können kein Bild auswählen, das nicht auf dem Bildschirm sichtbar ist, und das Backend muss nicht erraten, was sichtbar ist. Und das Zurückscrollen zum ausgewählten Bild schlägt jetzt in Grenzfällen viel seltener fehl. Das versteckte Problem war nicht ein einzelner kaputter Shortcut; es war, dass die Auswahl-Semantik in der falschen Schicht angesiedelt war.
Die Lösung besteht darin, klar zu trennen, was zu Backend- und Bibliotheksverwaltung gehört, von dem, was zu GUI-Zuständen und -Interaktionen gehört, und eine starre Schnittstelle zwischen beiden bereitzustellen. Das macht die verschiedenen Teile des Codes besser gekapselt, immun gegen Änderungen in den anderen Teilen und zentral verwaltet.
Hover- und Fokus-Störungen kamen von mehreren gültigen Systemen, die kollidierten
Maus-Hover, Tastatur-Hover, Fokus und Klickbehandlung waren jeweils aus legitimen Gründen implementiert, aber sie teilten sich keinen einzigen Schlichtungspunkt. Darktable hat jetzt sogar einen stolzen Marketing-Spruch im Hintergrund des Leuchttisches stehen, wenn die Sammlung leer ist, der jenen „klicklosen" Workflow anpreist, der es Ihnen ermöglicht, Bild-Metadaten zu überschreiben, ohne es zu wollen oder auch nur zu wissen. Das kommt mir vor wie ein Autohersteller, der ein Auto bewirbt, das so konstruiert ist, dass Sie das Lenkrad mit den Knien bedienen können (und Ihre Hände zum Biertrinken benutzen).
Deshalb konnte sich älteres Leuchttisch-Verhalten inkonsistent anfühlen, ohne dass es einen einzelnen offensichtlichen Fehler gab: Es war durch Design kaputt. Das Klicken auf eine Overlay-Schaltfläche, um das Bild zu bewerten, konnte auch das gesamte Vorschaubild auswählen, aber das Bewerten per Tastatur nicht. Tastaturbewegung und Mausbewegung konnten beide der Meinung sein, dass sie das „aktuelle" Bild besaßen, und alle möglichen destruktiven Metadatenänderungen auslösen, bloß durch das Überfahren des Bildes und ohne um Bestätigung zu bitten. Das Filtern von Bildern nach Sternebewertung und das versehentliche Ändern der Bewertung eines Bildes konnte es unerwartet verschwinden lassen. Ausgeblendete Widgets konnten immer noch hover-bezogene Logik empfangen. Der GTK-Fokus konnte das eine entscheiden, während die View-Schicht etwas anderes erwartete.
Die Korrekturen hier drehten sich weniger um das Hinzufügen neuer Funktionen als um die Wahl eines einzigen Eigentümers für den Zustand. Der Over-Zustand-Dispatch wurde zentralisiert. Das Tastatur-Durchsehen erhielt einen sinnvollen Ausgangspunkt und einen expliziten Abbruchpfad mit Shift+Ctrl+A. Die Fokusanzeige wurde explizit gemacht. Ausgeblendete Widgets nahmen nicht mehr an Hover-Logik teil, die sie nicht anzeigen konnten. Das Klicken auf Overlay-Schaltflächen sickerte nicht mehr in die Vorschaubild-Auswahl durch. Es werden keine Metadaten mehr (über)schrieben, ohne dass ein explizit eine Schaltfläche gedrückt wird, Hover-Ereignisse sind alle schreibgeschützt.
Ansel hat zwei einfache Regeln:
- jede Aktion, die (Meta-)Daten (über)schreibt, wird nur an explizit ausgewählten Bildern durchgeführt,
- eine explizite Auswahl erfolgt nur durch die Interaktion mit etwas „Hartem": einem Mausklick oder einem Tastaturanschlag.
Hover-Ereignisse sind dann schreibgeschützten Ereignissen vorbehalten.
Der Nutzen für die Benutzer ist, dass der Leuchttisch mehr wie ein einziges Interaktionsmodell reagiert. Das versteckte Problem war nicht, dass Hover kaputt war. Es war, dass mehrere Arten von Hover gleichzeitig „richtig" und daher gemeinsam falsch waren.
Änderungen am Backend
Das Umhängen des Thumbtables zwischen Leuchttisch und Filmstreifen
Eines der am tiefsten verborgenen Probleme war die Widget-Eigentümerschaft. In Darktable wurde dasselbe Thumbtable-Raster zwischen Leuchttisch- und Filmstreifen-Kontexten umgehängt. Auf dem Papier vermied das Code-Duplizierung und Widget-Wildwuchs, und das klingt clever. In der Praxis machte es Scroll-Zustand, Vorschaubild-Initialisierung, Garbage Collection und Ereignis-Lebensdauer davon abhängig, wo das Widget zuletzt gelebt hatte. Ganz zu schweigen davon, dass das Umhängen langsam war, sodass das Hin- und Herwechseln zwischen Leuchttisch und Dunkelkammer um etwa 1 s verzögert wurde, die Zeit, die GTK benötigt, um neue Vorschaubildgrößen neu zu berechnen, möglicherweise den Cache nach neuen Vorschaubildern zu fragen und Widgets neu zu zeichnen.
Das zeigte sich auf sehr konkrete Weise. Die Scroll-Position konnte springen oder inkonsistent werden. Vorschaubilder konnten zum falschen Zeitpunkt ausgeblendet, angezeigt oder zerstört werden. Die Garbage Collection wurde schwerer nachvollziehbar, weil die Widget-Hierarchie nicht stabil war. Natürlich wurde all das Commit für Commit gelöst, aber der Code blieb dabei in einem Zustand unwartbarer Komplexität.
Die eigentliche Lösung bestand darin, nicht länger zu versuchen, clever zu sein. Leuchttisch und Filmstreifen haben jetzt separate Thumbtables. Das bedeutet etwas mehr explizite Struktur im Code, aber weit weniger versehentliche Zustände. Der Nutzen für die Benutzer ist in ruhigerem Scrollen sichtbar, in weniger Layout-Störungen bei Ansichtswechseln und in einem Filmstreifen, der sich wie ein richtiges Geschwister des Leuchttisches verhält statt wie ein wiederverwendetes Fragment.
Menü- und Layout-Änderungen waren keine Kosmetik
Dass das globale Menü im April 2025 wirklich global wurde, war keine Branding-Übung. Zuvor waren viele Befehle so implementiert, als gehörten sie zu lokalen Modulen, obwohl sie in Wirklichkeit Aktionen auf Anwendungsebene waren. Einige von ihnen waren vollständig hinter Tastenkürzeln versteckt und nur denjenigen bekannt, die die Dokumentation lasen. Viele von ihnen entdeckte ich erst, als ich ihren Code entfernte. Dasselbe galt für Bildinformationen, Ansichtswechsel und alte View-Toolboxen. Sie lebten an Orten, an denen sich einmal leicht drumherum programmieren ließ, die aber über die Ansichten hinweg schwer kohärent zu warten waren.
Das Verschieben der Bildinformationen in die globale Menüleiste, das Verschieben des Ansichtswechslers an einen klareren Ort auf oberster Ebene und später das Entfernen der unteren mittleren Symbolleiste waren allesamt Folgen derselben Diagnose: Die Mitte der Oberfläche sollte der Bildinteraktion gehören, nicht historischen Überbleibseln. Das Entfernen der Zeitleiste und die Entscheidung, keinen leeren Filmstreifen anzuzeigen, sind kleinere Beispiele derselben Logik. Sogar dass Enter das ausgewählte Bild in der Dunkelkammer öffnet, passt hierher: Es bringt das Verhalten mit der primären Aufgabe des Leuchttisches in Einklang, statt zufällige historische Gewohnheiten zu bewahren.
Der Nutzen für den Benutzer ist nicht „das Menü ist hübscher". Er besteht darin, dass Befehle an Orten zu finden sind, die ihrem Geltungsbereich entsprechen, und dass der Leuchttisch selbst weniger Bedienelemente hat, die mit dem Raster konkurrieren. Ganz zu schweigen davon, dass „Module", die eigentlich (ungleichmäßige) Raster von Schaltflächen sind, nur getarnte Menüs mit schlechterem Design sind, sodass sie jetzt in Menüeinträge umgewandelt werden.
Das globale Menü war auch die Gelegenheit, Funktionen in die GUI zu bringen, die bislang nur in versteckten Shell-Skripten (!) lebten: das Vorladen der Vorschaubilder für die aktuelle Sammlung und das Entfernen von Vorschaubildern aus dem Disk-Cache. Diese wurden jahrelang in der GUI gewünscht, passten aber nirgendwo in Darktables modul-orientiertes UI-Design.
Aber… diese rechte Seitenleiste aus Modulen, die keine Menüs sein wollten, war ein Segen, weil sie viel Fläche wegnahm, die mit Bildern bemalt werden musste, und die Vorschaubilder kleiner machte. Ihr Entfernen ließ die schlechte Performance des Vorschaubild-Zeichnens in all ihrer Pracht zutage treten: Nichts wurde zwischengespeichert, weder Bildoberflächen noch Metadaten, und alles wurde ständig neu geholt. Die Neuentwicklung war unvermeidlich; die Architektur war kaputt.
Zoom wurde erst nutzbar, nachdem die Vorschaubildgrößen konsistent gemacht wurden
Der frühe Versuch eines zoombaren Leuchttisches scheiterte aus einem strukturellen Grund: Vorschaubild-Geometrie, zwischengespeicherte Mipmap-Größen und Navigation auf View-Ebene teilten sich nicht dasselbe Modell. Deshalb musste die Funktion zuerst entfernt werden. Hätte ich sie an Ort und Stelle weiter poliert, wäre das Ergebnis nur eine besser aussehende kaputte Funktion gewesen.
Die spätere Arbeit reparierte die Abhängigkeitskette in umgekehrter Reihenfolge. Das View-Backend erhielt das Gerüst für den Leuchttisch-Zoom, dann wurde dem Vorschaubild-Cache beigebracht, kohärenter über Vorschaubildgrößen zu argumentieren, eine API für Vorschaubilder in voller Auflösung wurde vorbereitet, dann wurden darauf aufbauend Clamping, Schwenken (Panning) und ein baryzentrum-basiertes automatisches Schwenken hinzugefügt. Erst als sich die unteren Schichten über Bildgröße und Abrufstrategie einig waren, wurde ein 200%-Zoom zu einer vertretbaren Funktion.
Der Nutzen für die Benutzer ist, dass der Zoom jetzt an den Cache und an die Navigation gebunden ist, statt mit ihnen zu kämpfen. Ziehen zum Schwenken, Shift + Ziehen über sichtbare Vorschaubilder hinweg und automatisches Schwenken zum Detail-Baryzentrum funktionieren alle, weil die zugrunde liegende Pipeline weiß, was gezoomte Vorschaubilder sein sollen.
Die verwirrende Bedeutung von „Zoom" im Leuchttisch wurde aktualisiert: Der Begriff „Zoom" wurde sowohl für äußeres Zoomen (Anzahl der Bilder pro Zeile, was indirekt ihre sichtbare Größe beeinflusst) als auch für inneres Zoomen (Vergrößerung innerhalb des Bildrahmens) verwendet. Jetzt haben wir also Spalten für den äußeren Zoom und Zoom für die Vergrößerung.

Stören Sie sich nicht am Kreuz anstelle des --Symbols in der Spalten-Spinbutton, es ist ein Fehler von GTK mit dem KDE/Plasma-Theme Breeze .
Dies ermöglichte es, eine Funktion umzusetzen, die mir schon lange im Kopf herumging: baryzentrisches automatisches Schwenken bei der Vergrößerung von Vorschaubildern.


Sie können sehen, dass beim Aktivieren des 100%-Zooms die Bilder automatisch an ihrem Inhalt ausgerichtet werden, obwohl sie völlig unterschiedliche Bildausschnitte und Seitenverhältnisse haben. Dies verwendet eine Merkmalsanalyse per Wavelet-Zerlegung, aus der wir die Koordinaten des Detail-Baryzentrums berechnen. Es ist nicht perfekt, weil es über die Bilder hinweg nicht denselben Teil des Gesichts erfasst, aber es liefert uns in allen Fällen das Gesicht. Das ist ein Projekt, das ich lange auf der langen Bank hatte, aber mit dem vorherigen Leuchttisch-Design war es nicht möglich.
Es versteht sich von selbst, dass das Ziehen in gezoomte Vorschaubilder wieder eingeführt wurde, und das Ziehen in alle gezoomten Vorschaubilder wurde ebenfalls hinzugefügt (Shift+Ziehen). Diese Funktion wurde in Darktable lange gewünscht, war aber in ihrem beschissenen Design eindeutig nicht möglich.
Selbstverschuldete Metadaten-Verlangsamung
Der alte Leuchttisch stellte der Datenbank immer wieder dieselben Fragen, ein Vorschaubild nach dem anderen. Bei einer kleinen Sammlung ist das leicht zu übersehen. Bei einer größeren zeigt es sich als Mikroruckler beim Öffnen der Ansicht, beim Anzeigen von Overlays, beim Überfahren von Gruppen oder beim Aktualisieren verlaufssensitiver Daten. Das versteckte Problem war nicht, dass rohes SQL in absoluten Zahlen langsam war; es war die Wiederholung und das Timing dieser Abfragen.
Die Lösung kam in Etappen. Schreibgeschützte Metadaten wurden begonnen, aus Bildstrukturen zwischengespeichert zu werden. Dann, im Februar 2026, konnten Metadaten für eine ganze Sammlung mit einer SQL-Abfrage statt einer Abfrage pro Bild abgerufen werden. Das Thumbtable konnte den Bild-Cache aus der Sammlung befüllen, die es gleich anzeigen würde. Das Caching der Vorschaubild-Informationen wurde in einen Helfer ausgelagert und später wieder in dt_image_t zusammengeführt, sodass Metadaten nicht zwischen parallelen Strukturen hin- und herspringen mussten. Sogar kleine Änderungen wie das Entfernen von History-Hash-Pings beim Neuzeichnen sind hier von Bedeutung, weil sie unsichtbare Roundtrips aus heißen Pfaden herausschneiden.
Eine Menge nutzloser SQL-Abfragen pro Vorschaubild wurde entfernt, ebenso wie SQL-Code im Leuchttisch-GUI-Code, sodass die funktionalen Schichten jetzt ordentlich getrennt sind.
Der Nutzen für den Benutzer ist genau: Das Öffnen großer Sammlungen stockt weniger, overlay-lastige Anzeigen zögern weniger, und Verlaufs- oder Metadaten-Aktualisierungen verbreiten sich mit weniger sichtbaren Pausen.
Der Mipmap-Cache musste ebenfalls repariert werden
Der Mipmap-Cache ist die Schicht, die RAW-Bilder und vorgenerierte Vorschaubilder in den RAM lädt und sie leert, wenn der Speicher knapp wird. Das war eine der tiefsten verborgenen Quellen sichtbarer Fehler. Wenn alles zusammenpasste, funktionierte es gut genug. Wenn die Quelldatei kleiner als erwartet war, wenn eine eingebettete JPEG-Vorschau eigenartig war, wenn der History-Hash undefiniert war oder wenn die Invalidierung des Disk-Caches den Bearbeitungen hinterherhinkte, erlebte der Benutzer nicht „einen Cache-Fehler". Er sah veraltete Vorschaubilder, kaputte Vorschauen oder Bilder, die sich weigerten zu aktualisieren.
Deshalb wirkt so viel der Arbeit von 2025 am Cache chirurgisch. Die Logik der Puffer-Allokation wurde neu geschrieben, weil die Eigentümerschaft zu verworren war. Die Behandlung von zu kleinen Eingaben wurde verbessert, weil Annahmen über die Eingabegröße zu weit durchsickerten. Eingebettete Vorschauen wurden nicht mehr so aggressiv verworfen, weil zu kleine Vorschauen für die Konsistenz über Zoomstufen hinweg immer noch nützlich waren. Sidecar-JPEG-Vorschauen wurden verwendet, wenn vorhanden, anstelle der im RAW eingebetteten Vorschaubilder. Die Cache-Invalidierung wurde gehärtet, und zwischengespeicherte Vorschaubilder wurden wirklich von der Festplatte entfernt, wenn sie es sollten. Neu generierte Mipmaps begannen, ihren Hash synchron mit dem Verlaufszustand in die Datenbank zurückzuschreiben, und der Fall eines undefinierten History-Hashs ließ Mipmaps nicht mehr ungespeichert.
Der heimtückischste Fehler, den ich fand, war in der unsinnigen Komplexität der Verflechtung von Thumbtable, View und Mipmap-Cache vergraben. Während das Rendern von Vorschaubildern aus Performance-Gründen in einen separaten Thread ausgelagert wurde (wie es sein sollte) und mehr als ein Thread gestartet werden konnte, um (angeblich) mehrere Vorschaubilder auf einmal zu verarbeiten (möglicherweise unter Verwendung mehrerer GPUs), konkurrierten alle Verarbeitungs-Threads und die GUI tatsächlich darum, die Mipmap- und Bild-Caches zu sperren, was die GUI nur ins Stocken brachte, während Pipelines liefen. Nur weil ich unter der Annahme arbeitete, dass ich die Dinge lehrbuchmäßig neu geschrieben hatte, und ich wusste, dass die lehrbuchmäßige Art schneller als das sein sollte, grub ich weiter, bis ich herausfand, warum es immer noch nicht so schnell war wie erwartet, und vereinfachte dabei alles Schicht für Schicht.
Der Nutzen für die Benutzer ist, dass der Cache vertrauenswürdiger ist. Nach Bearbeitungen zeigt der Leuchttisch seltener ein veraltetes Vorschaubild. Bei heiklen Dateien schlägt die Vorschau-Generierung seltener fehl. Bei wiederholten Besuchen derselben Sammlung verhält sich der Disk-Cache eher wie ein Cache und weniger wie ein veraltetes Screenshot-Archiv. Aber all das, ohne dass ständig alles aktualisiert/neu berechnet/neu gezeichnet werden muss, nur um sicherzugehen.
Außerdem wurden die Optionen, eingebettete JPEGs zu verwenden oder eine Neuberechnung zu erzwingen, von den Einstellungen in das globale Menü verschoben und können zur Laufzeit geändert werden:

Dies ist ein weiteres Beispiel, bei dem das Aufräumen und Vereinfachen des Backends den Weg für eine Frontend-Erweiterung und neue Funktionen ebnete, die einfach Sinn ergeben.
Thread-Sicherheit und Race Conditions
Ein klassischer Grund, warum diese Fehler so lange bestanden, ist, dass sie erforderten, dass der Benutzer schneller als der Code war: schnell scrollen, eine Ansicht verlassen, während Vorschaubilder noch erstellt werden, die Größe ändern, während eine Hintergrund-Oberfläche noch erzeugt wird, oder ein Widget schließen, kurz bevor ein Worker-Thread eine Aktualisierung einreicht.
Deshalb ist die Arbeit an der Thread-Sicherheit von 2025-2026 von Bedeutung. Das Abrufen von Vorschaubildern wurde tiefer in Hintergrund-Jobs verlagert, dann später explizit aufgeschoben, damit das Dunkelkammer-Rendering die Priorität behalten konnte. Das Zerstören von Vorschaubildern wurde an sicherere Aufräum-Punkte verlagert. Hintergrund-Jobs, die Vorschaubilder erzeugen, lernten, sich selbst abzubrechen, wenn das Widget, dem sie dienten, verschwand. Bildoberflächen wurden durch Mutexe geschützt. Freigegebene Zeiger wurden auf null gesetzt. Bildpuffer wanderten zu besser verwalteten Allokationspfaden. Der Sinn all dessen war nicht „mehr Threads"; es ging darum, alte Threads davon abzuhalten, in toten Zustand zu schreiben oder um Sperren zu konkurrieren.
Das für den Benutzer sichtbare Ergebnis sind weniger Segfaults, weniger zufällige Störungen beim schnellen Scrollen und weniger Fälle, in denen die Oberfläche unter Last mit sich selbst zu wettrennen scheint.
Auch mehrere Thread-Locks, die im Laufe der Jahre hinzugefügt wurden, um Probleme zu flicken, wurden entfernt. Viele waren überflüssig (zeigten es aber unter der wahnwitzigen Komplexität des Ganzen nicht), einige schadeten der Leistung aktiv, und alle verbargen schlechtes Design. Wir haben jetzt weniger Sperrpunkte, aber das Ergebnis ist verständlicher und robuster.
Als Folge davon sind Vorschaubild-Widgets nun vollständig gekapselte Objekte. Sie verwalten intern ihre eigenen Threads der Vorschaubild-Rendering-Pipeline, die direkt mit ihrer eigenen zwischengespeicherten Bildoberfläche interagieren, sodass sie diese selbst erzeugen oder beenden können. Dieses zwischengespeicherte Bild wird nur dann für ungültig erklärt, wenn sich die Bildhistorie ändert, was durch das Backend der Entwicklungshistorie explizit gemacht wird. Da das Vorschaubild-Widget seinen eigenen Zustand kennt (sichtbar oder nicht, Bildaktualisierung nötig oder nicht, Größe, Focus-Peaking-Modus usw.), wird es deutlich robuster, als wenn man all diese Dinge von höheren Schichten aus handhaben wollte, die nicht miteinander kommunizieren.
Dies war mit dem Design von Darktable nicht möglich, weil es fortwährend Vorschaubild-Widgets dynamisch zur aktuellen Leuchttisch-Ansicht hinzufügte bzw. daraus entfernte, abhängig von der Position der schwebenden Zeile, was ein Versuch war, die Verlangsamungen durch all die Threads zu bewältigen, die um den Cache-Zugriff konkurrierten. Aber außerdem versuchten die Vorschaubild-Widgets von Darktable, bei ihrer Erzeugung sofort ein Bild aus dem Mipmap-Cache zu holen, was die CPU- und Speicher-I/O-Auslastung in die Höhe schnellen ließ und die Benutzeroberfläche faktisch einfror. Doch dieses Problem zu „beheben", indem man die Lebenserwartung der Vorschaubild-Widgets verkürzte, machte es unmöglich, sie ihren eigenen Zustand intern verwalten zu lassen, sodass dies von höheren Schichten erledigt werden musste, die untereinander kommunizieren mussten, um Zustände zu aktualisieren, was an einigen Stellen tatsächlich geschah, aber auf Kosten unerträglicher Komplexität.
Stattdessen werden die Vorschaubilder in Ansel alle auf einmal initialisiert, holen sich aber träge (lazy) erst dann ein Bild aus dem Mipmap-Cache, wenn sie sichtbar werden, und speichern es dann intern zwischen. Dadurch bleibt das Scrollen durch den Thumbtable reaktionsschnell, selbst bei einer Sammlung von 500 Bildern, die alle gerade ihr Bild erzeugen.
Import-Vorschauen wurden als Nebeneffekt verbessert
Das Import-Fenster war von dieser Arbeit aus demselben Grund betroffen wie der Leuchttisch: Es benötigt schnelle Vorschau-Extraktion, Metadaten-Prüfung und ein vernünftiges Ausweichverhalten bei nur teilweise unterstützten Dateien. Sobald die Mipmap- und Vorschaubild-Pfade neu geschrieben waren, wurde das Laden der RAW-Vorschau im Import schneller, und auch die TIFF/DNG-Unterstützung verbesserte sich.
Dies ist ein gutes Beispiel dafür, warum die Neuentwicklung weit unten im Stack stattfinden musste. Wenn die zugrunde liegende Vorschau-Maschinerie ineffizient oder fragil ist, erben sowohl Leuchttisch als auch Import denselben Schmerz. Sobald diese Maschinerie neu geschrieben war, profitierten beide davon.
Globale architektonische Änderungen
In meinem vorherigen Artikel habe ich gezeigt, wie der Cache der Arbeits-Pipeline das Hin- und Herwechseln zwischen Leuchttisch und Dunkelkammer nahezu augenblicklich macht, weil das gesamte Bild nicht neu berechnet werden muss. Die hier an der GUI geleistete Arbeit löst dasselbe Problem der Verzögerungen beim Ansichtswechsel, aber auf GUI-Ebene. Es gibt also keine Verzögerung mehr beim Wechsel zwischen den beiden Ansichten.
Das ist wichtig, weil die Verzögerung des Ansichtswechsels als Ausrede benutzt wurde, um Funktionen (Module/Werkzeugkästen) zwischen Leuchttisch und Dunkelkammer zu duplizieren, was nur die Überladung der GUI erhöht. Diese gesamte Backend-Verbesserung ermöglicht es also, das GUI-Design zu verbessern, indem jede Ansicht auf eine einzige Aufgabe spezialisiert wird:
- Metadaten-Handhabung für den Leuchttisch (plus natürlich Culling),
- Bildbearbeitung für die Dunkelkammer.
Infolgedessen wurden die Werkzeugkästen Metadaten und Schlagwörter aus der Dunkelkammer entfernt.
Wie viele Codezeilen hat die Neuentwicklung eingespart?
Zählt man mit cloc nur die Nicht-Kommentar- und Nicht-Leerzeilen und vergleicht den letzten Baum vor dem 1. Januar 2022 mit dem aktuellen Baum für die hier besprochenen Hauptdateien, so hat die Neuentwicklung insgesamt 4.714 Codezeilen eingespart.
Der Umfang für diese Zählung ist:
- das gesamte Verzeichnis
data/themes/vor 2022, verglichen mit dem heutigendata/themes/ansel.css, src/common/mipmap_cache.[ch],src/dtgtk/thumbtable.[ch],src/dtgtk/thumbnail.[ch],src/views/view.[ch],src/views/lighttable.c,src/libs/collect.c,src/libs/tools/filter.c,- sowie Dateien, die durch die Neugestaltung oder die Bereinigung der Sammlungs-GUI vollständig gelöscht wurden:
src/dtgtk/culling.[ch],src/libs/tools/view_toolbox.c,src/libs/collect.hundsrc/libs/recentcollect.c.
Innerhalb dieses Umfangs sank der Gesamtwert von 15.257 Codezeilen vor 2022 auf heute 10.543. Die Neuentwicklung hat somit insgesamt 4.714 Codezeilen eingespart. Die größten Einsparungen ergaben sich durch das vollständige Löschen der Culling-Ansicht (src/dtgtk/culling.c) (-1.406 Zeilen), das Schrumpfen des Leuchttischs (src/views/lighttable.c) (-976), das Entfernen von 7 alten Theme-Stylesheets und ihr Zusammenfassen zu einem einzigen (-864 über data/themes/), das Schrumpfen von src/dtgtk/thumbnail.c (-460), src/dtgtk/thumbtable.c (-458), das Löschen von src/libs/recentcollect.c (-358) und das Schrumpfen von src/views/view.c (-314). Einige Dateien sind tatsächlich gewachsen, insbesondere src/libs/tools/filter.c (+215) und src/libs/collect.c (+157), denn ein Teil des Ziels bestand darin, Sonderfall-Verhalten wie Culling und alte Sammlungs-GUI-Zweige zurück in einfachere, gemeinsam genutzte Infrastruktur zu verlagern, statt sie in parallelen Ansichten und Nebenmodulen zu belassen.
Die Zeilenzahl ist für sich genommen keine Qualitätsmetrik. Viele Neuentwicklungen verschieben Code nur hin und her. Aber hier passt die Zahl zur Designänderung: weniger dupliziertes Verhalten, weniger parallele Ansichten, weniger kompensierende Hacks und weniger Stellen, an denen die GUI, die Ansichtsschicht und der Cache alle dasselbe Problem doppelt lösen mussten.
Alles, was ich behoben habe, habe ich behoben, indem ich die Logik und den Code vereinfacht habe. Es war keine Notlösung erlaubt.
Benchmarks
Alle Laufzeiten wurden auf einem Lenovo ThinkPad P51 Laptop berechnet (Intel Xeon CPU E3-1505M v6 @ 3,00 GHz, Nvidia Quadro M2200 GPU mit 4 GB VRAM, 32 GB RAM, 4K-Display), CPU im Leistungsmodus, Linux Fedora 41 mit KDE/Plasma-Desktop. Die Laufzeiten der Pixel-Pipeline werden nicht verglichen (außerhalb des Umfangs; siehe den vorherigen Artikel). Ansel Master wird zum Commit 09749f1d (21. Feb. 2026) genommen.
| Beschreibung | Ansel Master | Darktable 5.0 |
|---|---|---|
| Zeit vom App-Start bis zum Zeichnen des letzten Leuchttisch-Vorschaubilds (gleiche Sammlung) | 2,12 s | 7,49 s |
| Zeit für den Wechsel vom Leuchttisch zur Dunkelkammer (gleiches Bild) | 0,2 s | 1,2 s |
| Zeit zum Durchscrollen (Anfang->Ende) derselben Sammlung von 471 Bildern* | 0,7 s | 5,0 s |
*: Vorschaubilder in beiden Fällen im Festplatten-Cache vorgeladen, 5 Vorschaubild-Spalten pro Zeile, 4K-Auflösung, keine rechte Seitenleiste.
Als „Behebung" hat uns Darktable 5.x mit einem prächtigen Splash-Screen beglückt, was mehr ein Geständnis als alles andere ist.
Die folgenden Werte wurden im Akkubetrieb gemessen, im Energiesparmodus, mit der Anwendung im Leerlauf (keine Benutzerinteraktion) über 5 Minuten, mit Intel Powertop. Der Grundverbrauch des gesamten im Leerlauf befindlichen Betriebssystems beträgt 1,6 % CPU. (Die Leistung ist nur für die App angegeben, % CPU für das gesamte System):
| Ansicht | Ansel Master | Darktable 5.0 |
|---|---|---|
| Leuchttisch | 1,8 % CPU, Leistung: 0,85 mW | 2,7 % CPU, Leistung: 103 mW |
| Dunkelkammer | 1,8 % CPU, Leistung: 7,65 mW | 1,8 % CPU, Leistung: 22 mW |
Diese Zahlen stellen den Grundstromverbrauch der GUI allein dar (GTK, Hintergrund-Worker, geplante Timer usw.). Darktable verliert Leistung über die GUI, und die mühsame Arbeit, die 2023-2024 an der Optimierung der Pixelverarbeitungsmodule für zusätzliche 15-50 ms geleistet wurde, ist völlig belanglos.
Schlussfolgerung
An diesem Punkt bin ich fest davon überzeugt, dass das Darktable-„Projekt" nur „Entwickler" anzieht, die nicht in der Lage wären, eine kognitive Unterscheidung zwischen GUI und Backend zu treffen, selbst wenn ihr Leben davon abhinge. Also werden GUI-Probleme im Backend gelöst, Backend-Probleme werden in der GUI gelöst, und dadurch wächst die Codekomplexität mit der Zeit außer Kontrolle, was später rechtfertigt, neue Funktionen hinzuzufügen, indem man mit möglichst wenig Code in eine Codebasis hineinhackt, die niemand mehr versteht. Ganz zu schweigen davon, dass nichts davon dokumentiert war, sodass ich es über mehrere Jahre mühsam per Reverse-Engineering nachvollziehen musste, rekursiv ein bisschen hier und ein bisschen da vereinfachend, bis es schließlich zu einer sauberen Gesamtlogik konvergierte.
Es versteht sich von selbst, dass alle Notlösungen und „Quick Fixes", die hinzugefügt wurden, entfernt wurden. Sie alle stammten aus der Zeit nach 2020, was einen besorgniserregenden Trend in der Verschlechterung der Codequalität zeigt.
Dieses Bereinigungsprojekt hat mir 4 Jahre meines Lebens gestohlen, mir keine Freude bereitet, und die Menschen, die all die Regressionen eingeführt haben, die ich mühsam behoben habe, müssen für die Folgen ihres Handelns zur Verantwortung gezogen werden. Es besteht ein riesiger Unterschied zwischen nicht genug Zeit zu haben, um Dinge ordentlich zu erledigen, und viele Arbeitsstunden zu verbrauchen, um die Dinge schlimmer zu machen. Und dann sich zu weigern zuzugeben, dass man die Dinge schlimmer gemacht hat, sich zu weigern zu akzeptieren, dass es ein Problem gibt, und seinen Bestätigungsfehler zu nähren, indem man nur auf das Feedback zufriedener Nutzer hört, ist der ultimative Beweis von Dummheit.
Darktable ist zu Scheiße verkommen, und ich habe gerade technisch erklärt, warum. Das Darktable-Team und sein Beiwagen-Forum von Tech-Bros möchten die Leute glauben machen, dass ich wütend auf sie wurde, weil sie meine Änderungen nicht annehmen wollten, und dass alles ein zwischenmenschliches Problem sei. Für den Laien, der nicht versteht, was ich hier und in den vorherigen Artikeln geschrieben habe, ist es leichter, an zwischenmenschlichen Ärger zu glauben, als zu verstehen, wie eine Abfolge schlechter technischer Entscheidungen über mehrere Jahre hinweg mich zum Opfer der Probleme machte, die sie geschaffen hatten, weil ich hier der Einzige in Vollzeit war und meinen Lebensunterhalt davon bestritt. Ich wurde wütend auf sie, weil sie mir immer wieder vor die Haustür schissen und ich wiederholt aufräumen musste. Das ist eine Form von Gewalt, die wirklich schwer zu erkennen und zu benennen ist, weil sie sich nicht materiell manifestiert: Es ist eine Art, dir das Leben schwerer zu machen, täglich, Schritt für Schritt, nur weil ein Haufen amateurhafter Männer mittleren Alters ohne Fähigkeiten Teil von etwas Coolem sein wollte, ohne die schädliche Wirkung ihrer Beiträge auf das gesamte Projekt zu erkennen.
Und es fiel mir zu, das Chaos aufzuräumen, weil ich offenbar der Einzige war, der sich um die Regressionen kümmerte, um die seltsamen zufälligen Bugs und Abstürze, um die schlimmsten GUI-„Innovationen", die selbst meine eigene Frau davon abhielten, Darktable zu benutzen, weil es einfach überwältigend ist, und um all die neuen Verlangsamungen, die sich über die Jahre immer weiter anhäufen. Wie Chris Elston 2022 im Darktable-IRC-Chat zu mir sagte, bevor ich ihn für immer verließ, über Dinge, die ich 2019 behoben hatte und die sie 2022 wieder kaputt machten: „shut up and fix it". Wenn das keine Gewalt ist, dann weiß ich nicht, was Gewalt ist. Und jetzt versuchen sie, das Gerücht zu verbreiten, dass ich der Toxische war. Das ganze Team ist toxisch. Ihre nachlässige Arbeitskultur ist toxisch. Ihre Art, sich für alles zu begeistern, solange es neu ist, ohne über die Wartungskosten, die Funktionsduplizierung und die allgemeine Überforderung der Nutzer nachzudenken, ist toxisch. Ihr Mangel an Sorge um die Zukunft des Projekts und um die Folgen der Entscheidungen, die sie treffen, ist toxisch.
Und was besonders toxisch ist: Auf discuss.pixls.us wird jeder Beitrag, der mich lobt oder Ansel lobt, gemeldet und ausgeblendet. Es gibt keine Meinungsfreiheit in einem Forum für freie Software. Sie haben den Kommunismus einfach in Stalinismus verwandelt.
Translated from English by : ChatGPT, Claude. In case of conflict, inconsistency or error, the English version shall prevail.