Device-Independent-Bitmaps

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:

1HDC desktop = GetDC(NULL); /* get default-DC from Screen */
2HDC mydc = CreateCompatibleDC(desktop);
3HBITMAP mybitmap = CreateDIBitmap(desktop, /* <- use desktop-DC  */
4    &bitmapHeader, CBM_INIT, init_buffer,
5    &infoheader, DIB_RGB_COLORS);
6...

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.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!