
Device-Independent-Bitmaps
« | 03 Feb 2024 | »Seit 15 Jahren nutze ich DIBSections
der
WinAPI
um Grafiken in GUI Apps zu verwalten und mit der Zeit hinterfragt man gar
nicht mehr, was alles ausimplementiert ist, wie es ist
… man kopiert es einfach.
Doch DIBSections
gab es in
Windows 3.x
nicht und auch unter NT 3.5
funktionieren einige meiner Codes nicht.
Frage: Warum?
Das GDI ist vermutlich die älteste API in Windows, die sich bis heute am wenigsten verändert hat … bzw. die heute noch so funktioniert, wie man es in den 80er Jahren für richtig gehalten hat.
Mit den erwähnten DIBSections kam um das Jahr 1995 eine zusätzliche
“modernere” Schnittstelle hinzu, die eigentlich bis heute “ausreicht”.
Device-Independent-Bitmaps (DIBs) sind Rastergrafiken, wo der Nutzer
entscheidet, wie sie intern aufgebaut sind. Man kann Farbpaletten für
4 oder 8 Bit Grafiken definieren, oder gleich 16- oder 24-bit RGB
Farbwerte speichern. (Später kam noch 32-bit RGBA mit Transparenz hinzu.)
Eine solche “unabhängige” Bitmap wurde den WinAPIs übergeben und dieses zeichnete alles passend auf Bildschirme, in den Speicher oder auf den Drucker.
Doch das war nicht immer so …
Am Anfang war das Bit.
Bitmaps waren mal 1-Bit Grafiken, also rein schwarz-weiß. X11 unterscheidet daher folgerichtig Bitmaps für 1-Bit-Bilder und Pixmaps für Farbpixel-Bilder. Und aus dieser Zeit stammt der Standard, dass eine neue Bitmap mit einem Bit pro Pixel erzeugt wird.
DIBs gab es auch schon von Anfang an und sie wurden per CreateDIBitmap()
über ein bestehendes Device-Context-Handle (HDC
) erzeugt. Danach konnte man
sie über den HDC
per GetPixel
und SetPixel
und ein paar andere Linien- und
Polygon-Zeichenroutinen befüllen.
Und das war so f*cking langsam, weshalb man per CreateDIBSection()
das
gleiche nochmal implementierte, und zusätzlich den Pointer zu den Rohdaten
im Speicher bekam.
Die Coder erzeugten dann nur noch 32-bit DIB-Sections und manipulierten sich
die Pixel selbst so hin, wie sie es brauchten, und umgangen so das langsame
umwandeln von Pixel-Operationen in GDI Device-Contexts.
Früher musste man ohne DIB-Section entweder alle Pixel bereits mit
CreateDIBBitmap()
dem neu entstehenden Objekt eingravieren, oder mit den
genannten GDI-Zeichenprimitiven dann drübermalen.
Finde den Fehler:
Und so sah also einer meiner Retro-Codeblöcke für Win3X aus, der auch auf heutigen System noch lauffähig sein muss:
1HDC desktop = GetDC(NULL); /* get default-DC from Screen */ 2HDC mydc = CreateCompatibleDC(desktop); 3HBITMAP mybitmap = CreateDIBitmap(mydc, 4 &bitmapHeader, CBM_INIT, init_buffer, 5 &infoheader, DIB_RGB_COLORS); 6HGDIOBJ backup = SelectObject(mydc, mybitmap); 7/* draw into bitmap */ 8SetPixel(mydc, x, y, RGB(r, g, b)); 9/* cleanup */ 10SelectObject(mydc, backup); 11DeleteObject(mybitmap); 12DeleteDC(mydc); 13ReleaseDC(NULL, desktop);
Und egal wo ich meine so erstellte mybitmap
ausgeben wollte, ich sah immer
nur eine Schwarz/Weiß-Grafik.
Der Fehler liegt in
mydc
.
Wenn man einen “compatible DC” erzeugt, hat der wie vor 40 Jahren eine
1-Bit Bitmap mit 1x1 Pixel drinnen und wenn man dann CreateDIBitmap
aufruft, erzeugt das eine Bitmap, die kompatibel mit dem DC ist und
konvertiert meine “externen” DIBitmap-Daten in das Zielformat, und so
wird ein 1-bit Abbildung daraus.
Lösung:
Anstatt mydc
in CreateDIBitmap
zu nutzen, kann man dort den DC des
Bildschirms nutzen. Der sollte nämlich genau die grafischen Eigenschaften
haben, wie es gerade in der Grafikkarte eingestellt ist.
Man erhält also eine Bitmap, die genau das Farbschema hat, das gerade
angezeigt werden kann und die eigenen Bitmapdaten werden in genau dieses
Format konvertiert.
Fazit
Tja, der wahre Unterschied zwischen CreateDIBitmap
und CreateDIBSection
ist, dass CreateDIBitmap
wie CreateCompatibleBitmap
ein zum DC kompatible
Bitmap erzeugt und CreateDIBSection
die Bitmap so herstellt, wie man es
angegeben hat.
Die Sache ist sogar noch schlimmer, denn mir ist der Fehler auch mit
CreateDIBSection(mydc, ...)
unterlaufen.
Es sieht aber so aus, als würden neuere Windows Varianten beim Zeichnen, dann
das Format der DIB-Section nutzen, und nicht das Format des DCs, das wegen
CreateCompatibleDC
nur 1 Bit breit ist.
Ältere Windows Versionen sind da aber strenger … und jetzt erklärt es sich mir auch, warum mein OpenGL-Code unter Windows 95 nur Schwarz-Weiß Ergebnisse im Test lieferte. Ein weiterer Fix steht bevor.
Wichtige Erkenntnis: Windows korrigiert Fehler!
Und deshalb programmieren wir schlampig … weil uns die Fehler nicht als solche auffallen.