
Bildausschnitte per Line-Padding
« | 28 Apr 2024 | »Die Arbeit mit meinem Book8088
inspirierte mich mein Grafik-Konzept zu überdenken:
Ein Rasterimage ist nicht ein Array of Pixels
,
es ist ein Array of Pixel-Lines
.
Und daraus folgt: Ein Bildauschnitt ist
Originalbild
+ Offset
+ Padding
Ich wollte im GATE Framework “ein” generisches Bildformat haben,
und das sollte RGBA (32-bit) sein.
Jedes beliebige Format kann man nach RGBA konviertieren und somit
wird die Bildmanipulation nur noch auf RGBA durchgeführt.
Doch dann kam DOS.
Über die unterschiedlichen komplizierten Speicherformate von EGA/VGA/SVGA usw. Bildern könnte man Bücher schreiben und es ist ein Segen, dass wir uns in Windows, Linux, X11 oder OpenGL nicht mehr um solche Details kümmern müssen.
Der Mode 13
war der einzige VGA-Modus, in welchem 256 Farben als lineare
Paletten-Pixel Werte im Speicher abgelegt werden konnten und so mit
einer Auflösung von 320 x 200 Pixel ein bisschen Grafik am DOS-PC ohne
Aufwand produziert werden konnte.
Und genau aus diesem Grund sind 8-Bit Farbpaletten Grafik neben RGBA nun
ein weiteres “generisches” Format, dass ich unterstütze.
Denn so passt ein Bildschirm-Inhalt in ein einziges 64-KB Segment.
Performance Problem
Doch die Idee, ich könnte einen Framebuffer mit den vollen 320 x 200 Pixeln
bearbeiten und dann als ganzes ins Video-RAM (0xa000:0
) kopieren, scheitert
am 8 MHz Prozessor: Denn das dauert ewig … aus Sicht des Gamers.
Dass das Kopieren von 64000 Bytes messbar lange dauert, kann sich heute kaum
jemand mehr vorstellen.
Doch die Lösung ist oft einfach: Man überträgt nur Änderungen.
Bei Spielen ändert sich nicht immer gleich der ganze Bildschirm-Inhalt, es
werden meist nur einige wenige Figuren verschoben.
Doch wenn man jetzt ein großes Bild in einzelne separate Teilbilder zerlegt,
fragmentiert und vergrößert man den Speicherverbrauch.
Ein besseres Pixel-Layout
Was wäre, wenn man (wie bei Strings und String-Views) den Inhalt eines Speicherblocks mehreren Konsumenten gleichzeitig anbietet?
Man “verkauft” Ausschnitte eines großen Bildes als mehrere Teilbilder, obwohl die nur das Originalbild “mit anderen Abmessungen” benutzen.
Bisher bestand ein Bild bei mir aus einer Breiten- und Höhenangabe
und einem Pointer auf die Rohdaten.
Pixelpositionen ergaben sich nach der Formel:
pixel_pointer = start_pointer + y * width + x
Führt man aber noch einen line-padding
Wert ein und betrachtet ein
Bild nicht als fixen Block, sondern als ein Array von Zeilen, dann
bekommt man eine Bildzeile per:
pixel_pointer = start_pointer + y * (width + padding) + x
Man muss jetzt zwar bei jedem Zeilenwechsel (y) den Pointer neu berechnen, doch nun können Unterbilder über Pointer und Padding auf das Originalbild verweisen.
Neues altes Interface
Mit diesem Update kann ich nun beliebige Raster-Rahmen-Objekte für die gleichen Bilddaten erstellen und Reference-Counting sichert die korrekte Nutzung und Freigabe der Ressourcen.
Die einzige Änderung ist, dass man nun nicht mehr das Bild als einen fetten Puffer bearbeiten kann, sondern immer nur Zeilenweise.
Das ist tatsächlich auch wieder ein Overhead, aber dieser fällt hier
wenig bis gar nicht auf.
Denn anstatt in separate Speicher zu zeichnen und diese dann
zusammenkopieren zu müssen, wird alles in einen Block (aber an die richtige
Stelle) geschrieben.
Fazit
Und wieder hat mir die “alte Welt” eine Lehre erteilt.
Man muss nicht Megabytes an Daten generieren und dann wieder wegwerfen,
sondern man kann durch “Kooperation” mit einem Bruchteil der Ressourcen
zum gleichen Ergebnis kommen.
Oder anders formuliert:
- Wir brauchen weniger Speicher
- Wir nutzen daher Caches besser
- Wir reduzieren Kopien drastisch
- Wir erhöhen den Durchsatz
- Wir reduzieren den Strombedarf
… das bräuchten wir auch bei der Klima- und Umweltdebatte.