DirectDraw und 16bit-Farben
Sicherlich ist der ein oder andere schon einmal über das Problem gestolpert, da hat man in einem Grafikprogramm eine Farbe gemischt, z.B. RGB(254, 155, 65), setzt diese in einem Spiel ein und es kommt eine ganz andere Farbe heraus. Warum ?
Nun, eine 24bit-Farbe kann nicht so ohne weiteres in einer 16bit-Umgebung verwendet werden.
16bit Modus
24bit zu 16bit konvertieren
Was bedeutet 24bit und 16bit
Ein Bit ist die kleinste mögliche Einheit einer Information und kann die Werte 1 oder 0 speichern. Ein Byte ist gleich 8 Bits und alle zusammen bilden eine 8-stellige Binärnummer.
Ein Byte kann also einen Wert von 00000000 (das ist 0 in Dezimal) bis zu 11111111 (das ist 255 in Dezimal) speichern. Jede Farbe besteht aus einer Kombination der drei Farben Rot, Grün und Blau, dies nennt man auch Farbanteile.
24bit Farben
Im 24bit Modus kann ein jeder Farbanteil einen Wert in der Größe von 8 Bit speichern:
8 Bit für Rot,
8 Bit für Grün,
8 Bit für Blau ( 8 + 8 + 8 = 24 Bit).
Das bedeutet ein jeder Farbanteil hat einen möglichen Wert von 0 bis 255. Das ist der Grund warum im Visual Basic bei RGB(rot, grün, blau) für r, g und b ein Wert zwischen 0 und 255 gesetzt werden kann. Mit einer kleinen Berechnung erhält man die möglichen Kombinationen:
256 * 256 * 256 = 16.777.216 verschiedene Farben
Im 24bit Modus geht das noch mit Dezimalwerten, aber im 16bit Modus ist es besser mit Binär-Werten zu arbeiten. Der Farbwert ist eindeutig und setzt sich aus den Werten der drei Farbanteile zusammen wie z.B. die Farbe Magenta:
Farbe RGB-Wert Binärwert
rot 255 11111111
grün 0 00000000
blau 255 11111111
Der Farbwert ist dann 111111110000000011111111, das entspricht 16.711.935 Dezimal, was auch die Funktion RGB(255, 0, 255) zurückgibt.
Das gilt für jede andere Farbe, so z.B. für Grau:
Farbe RGB-Wert Binärwert
rot 128 10000000
grün 128 10000000
blau 128 10000000
und der Farbwert ist 100000001000000010000000, Dezimal 8.421.504.
Farben im 24bit Modus sind nicht kompliziert, im 16bit Modus sieht das schon ganz anders aus.

  16bit Modus [ Top ]
Das Komplizierte am 16bit-Modus ist, dass es bei den Grafikkarten zwei unterschiedliche Modi gibt. Der eine Modus verwendet 5 Bit für Rot, 6 Bit für Grün und 5 Bit für Blau (5 + 6 + 5 = 16 Bit). Dieser 16bit Modus wird auch einfach 565 genannt. Ältere Grafikkarten arbeiten anders und verwenden 5 Bit für Grün und ein Bit bleibt übrig (5 + 5 + 5 = 15 Bit). Dieser Modus wird 555 genannt.
In beiden Modi 555 und 565 wird eine Farbe wie im 24bit Modus zusammengesetzt, und so sieht die Zusammensetzung der Farbe RGB(15,12,29) im Modus 555 aus:
Farbe RGB-Wert Binärwert
rot 15 01111
grün 12 01100
blau 29 11101
Farbwert = 011110110011101, Dezimal = 15.773
und im Modus 565:
Farbe RGB-Wert Binärwert
rot 15 01111
grün 12 001100
blau 29 11101
Farbwert = 0111100110011101, Dezimal = 31.133
Im 16bit Modus liefert die Funktion RGB(r, g, b) nicht die gleiche Farbe zurück wie im 24bit Modus. Die Funktion arbeitet nur für 24bit Farbwerte. Die einzigen gleichen Farben sind weiß = RGB(255, 255, 255) und RGB(0, 0, 0) für schwarz.
Wenn also ein Spiel oder eine Anwendung im 16bit Modus erstellt wird, ist es sinnvoll nach der Initialisierung von DirectDraw die Farbreihenfolge und den Modus der Grafikkarte zu ermitteln und zu berücksichtigen.
Zunächst werden ein paar hilfreiche Variablen deklariert.
 
 'Farb-Reihenfolge Enumeration
Public Enum COLOR_SEQUENZ
  C_RGB = 1 'Die Farbreihenfolge ist RGB
  C_BGR = 2 'Die Farbreihenfolge ist BGR
End Enum

 '16bit mode format Enumeration
Public Enum c16BIT
  Mode555 = 1 'Das 16bit Format ist 555
  Mode565 = 2 'Das 16bit Format ist 565
End Enum

Public RGBForm As COLOR_SEQUENZ
Public Mode16 As c16BIT
Public GruenMax As Integer 'maximaler Grünanteil
Public BitDepth As Integer 'Farbtiefe
Public GBitMask As Single 'Grün Bit Maske
Public RBitMask As Single 'Rot Bit Maske
Public BBitMask As Single 'Blau Bit Maske

 'RGB-Farbanteile Benutzerdefinierter Datentyp
Public Type cRGB
  r As Byte
  g As Byte
  b As Byte
End Type
 
1)  Als Erstes wird die Farbtiefe ausgelesen um zu wissen ob es 24bit oder 16bit ist.
2)  Feststellen welche Farbreihenfolge die Grafikkarte verwendet, RGB oder BGR.
3)  Wenn es 16bit ist, feststellen welchen Modus die Grafikkarte verwendet, 555 oder 565.
Farbreihenfolge RGB oder BGR
Was ist eine Farbreihenfolge ?
Die Farbreihenfolge legt fest an welcher Stelle die Farbbits stehen, um eine Farbe zusammensetzen zu können. So ist z.B. bei 16bit und Modus 555:
Farbe RGB-Wert Binärwert
rot 12 01100
grün 30 11110
blau 19 10011
Ist die Farbreihenfolge RGB, dann setzt sich der Farbwert wie folgt zusammen:
Farbwert: 011001111010011 oder in Dezimal = 13.267
Ist die Farbreihenfolge aber BGR, dann setzt sich der Farbwert wie folgt zusammen:
Farbwert: 100111111001100 oder in Dezimal = 20.428
Wie man sieht, geht bei der Farbreihenfolge BGR Blau an die erste Stelle (höchster Wert) und Rot an die Letzte (niedrigster Wert), und bei RGB ist Rot an erster Stelle (höchster Wert).
Daher sollte die Farbreihenfolge immer geprüft werden, da ansonsten die gewünschte Farbe nicht erreicht wird.
Farbmasken und Farbanteil
Farbmasken sind spezifische Nummern, die verwendet werden, um die Bits der Farbanteile eines Farbwertes zu erhalten. Einfach ausgedrückt, um den Anteil an Rot, Grün und Blau einer Farbe zu bekommen.
16bit, Modus 565, RGB Farbe dezimal 43.236:
Farbe RGB-Wert Binärwert
rot 228 10101
grün 168 000111
blau 0 00100
ist Binär 1010100011100100 
So können die Farbanteile leicht ermittelt werden, was einfach für das Auge erscheint, nicht jedoch für die Codierung.
Um einen Farbanteil aus einem Farbwert auszuschneiden, braucht man einige binäre Berechnungen, und an dieser Stelle kommen die Farbmasken ins Spiel. Im 16bit 565 RGB-Modus hat Rot die Maske F800 (hexadezimal),  63.488 dezimal oder binär 1111100000000000.
Die Verwendung von Hexwerten ist einfacher, so müssen nicht die Kolonnen von 1er und 0er getippt werden. In Visual Basic werden Hexwerte mit dem Präfix &H geschrieben. F800 ist in VB &HF800.
In boolescher Mathematik sieht eine Addition wie folgt aus:
Null oder Eins addiert zu Null ergibt Null. Der einzige Weg um eine Eins zu erhalten ist wenn X und Y 1 ist. Das ist die Logik hinter der Farbmaske. Es hat eine Eins wo die Bits des Farbanteiles stehen und Nullen bei den anderen. Auf diese Art kann der Farbanteil Rot aus dem Farbwert isoliert werden.
Rechnet man auf den Farbwert die rote Bitmaske wird der rote Farbanteil isoliert.
Jetzt gibt es nur noch das Problem mit den restlichen Nullen die nicht benötigt werden. In C++ gibt es dafür das "byte shifting", in VB muss das per Hand gemacht werden.
Diesbezüglich wird das Resultat dividiert. Das bedeutet, um 5 Bit abzuschneiden wird mit 100000 dividiert, um 13 Bit abzuschneiden mit 10000000000000, usw.
In unserem Beispiel sollen die ersten 5 Bit erhalten bleiben. Es sind daher 11 (16 - 5 = 11) Bit abzuschneiden. Daher wird mit 100000000000 dividiert.
Und schon ist der rote Farbanteil ermittelt. Der gleiche Vorgang gilt auch für Grün, bei Blau ist es nicht notwendig, da ja dahinter keine Bits mehr sind.
Nun aber genug von der Theorie, hier ist der entsprechende Source-Code:
 
Public Sub GetColorMode(ddPS As DirectDrawSurface7)
  Dim ddsd As DDSURFACEDESC2

   'Über die PrimarySurface erhalten wir die Informationen
   'der Farben, der Farbtiefe usw.
  ddPS.GetSurfaceDesc ddsd

  With ddsd.ddpfPixelFormat
     BitDepth = .lRGBBitCount  'Auslesen der Farbtiefe
      'Farbreihenfolge prüfen ... RGB oder BGR
     If .lRBitMask > .lBBitMask Then
       RGBForm = C_BGR
     Else
       RGBForm = C_RGB
     End If
      'Handelt es sich um 16bit Farben, dann müssen
      'wir den Modus feststellen. 555 oder 565
      'Die lGBitMask (Green Color Mask) ist &H7E0 beim
      '565-Modus und &H3E0 beim 555-Modus
     If BitDepth = 16 Then
       If .lGBitMask = &H7E0 Then
         Mode16 = Mode565 '565 Format
         GruenMax = 63    'maximaler grüner Farbanteil
       Else
         Mode16 = Mode555 '555 Format
         GruenMax = 31    'maximaler grüner Farbanteil
       End If
     End If
      'Auslesen der FarbBitmasken
     GBitMask = .lGBitMask
     RBitMask = .lRBitMask
     BBitMask = .lBBitMask
  End With
End Sub
 
Berechnen einer 16bit Farbe anhand der Farbanteile Rot, Grün und Blau. Die Farbanteile werden mit der Bitmaske multipliziert und mit dem logischen Operator "OR" zu einem Farbwert kombiniert.
 
Public Function Get16BitColor(Color As cRGB) As Long
  Dim bsG As Single
  Dim bsR As Single
  Dim bsB As Single

   'bsR, bsG und bsB beinhalten die Anzahl
   'abzuschneidender Bits ("shifting")

   'die Grüne shifting-Variable ist bei Modus 555 und 565 gleich
  bsG = &H20
    If Mode16 = Mode555 Then
      If RGBForm = C_BGR Then
         'ist die Reihenfolge BGR hat Rot den höchsten
         'und Blau den niedrigsten Wert
         'im 555 Modus müssen 10 bits abgeschnitten werden
         'um den Rotanteil zu erhalten
        bsR = &H400
        bsB = 1
      ElseIf RGBForm = C_RGB Then
         'bei RGB genau umgekehrt
        bsB = &H400
        bsR = 1
      End If
    ElseIf Mode16 = Mode565 Then
      If RGBForm = C_BGR Then
         'im 565 Modus sind es 11 bits
        bsR = &H800
        bsB = 1
      ElseIf RGBForm = C_RGB Then
        bsB = &H800
        bsR = 1
      End If
    End If

    'mit dem logischen Operator OR kombinieren wir
    'die einzelnen Farben zu einem Farbwert
  Get16BitColor = Color.r * bsR Or Color.g * bsG Or Color.b * bsB

End Function
 
Ein Beispiel zu 16-bit Farben mit DirectDraw ermitteln können Sie hier downloaden. Dieses Beispiel finden sie auch mit ausführlicher Code-Abbildung in unserer Tipp-Rubrik.

Download  (4,9 kB) Downloads bisher: [ 1125 ]


  24bit zu 16bit konvertieren [ Top ]
Bevor es ans Konvertieren geht noch schnell eine sehr nützliche Funktion deren Prinzip schon weiter oben erklärt wurde. Um die Farbanteile einer 16bit Farbe zu ermitteln muss man nur den Farbwert mit der FarbBitMaske mittels AND addieren und die nicht benötigten Bits abschneiden.
 
Public Function GetRGB16(Color As Long) As cRGB
  Dim bsG As Single
  Dim bsR As Single
  Dim bsB As Single

   'bsR, bsG und bsB beinhalten die Anzahl
   'abzuschneidender Bits ("shifting")
   'die Grüne shifting-Variable ist bei Modus 555 und 565 gleich
  bsG = &H20
  If Mode16 = Mode555 Then
    If RGBForm = C_BGR Then
       'ist die Reihenfolge BGR hat Rot den
       'höchsten und Blau den niedrigsten Wert
       'im 555 Modus müssen 10 bits abgeschnitten
       'werden um den Rotanteil zu erhalten
      bsR = &H400
      bsB = 1
    ElseIf RGBForm = C_RGB Then
       'bei RGB genau umgekehrt
      bsB = &H400
      bsR = 1
    End If
  ElseIf Mode16 = Mode565 Then
    If RGBForm = C_BGR Then
       'im 565 Modus sind es 11 bits
      bsR = &H800
      bsB = 1
    ElseIf RGBForm = C_RGB Then
      bsB = &H800
      bsR = 1
    End If
  End If
   'Um die einzelnen Farbanteile einer 16bit-Farbe
   'zu erhalten, wird der Farbwert mit "And" dem
   'Bitmaske addiert und durch
   'die shifting-Variable dividiert.
  With GetRGB16
     .r = (Color And RBitMask) / bsR
     .g = (Color And GBitMask) / bsG
     .b = (Color And BBitMask) / bsB
  End With

End Function
 
Aber nun zum eigentlichen Konvertieren von 24bit zu 16bit. Es gibt immer wieder die Situation, dass man eine RGB-Farbe aus einem Grafikprogramm (z.B. Paint) hat, und diese in seinem Spiel einsetzen möchte, oder der Anwender soll eine RGB-Farbe direkt eingeben können. Wie auch immer, wenn das Spiel den 16bit Modus verwendet, muss diese Farbe zu 16bit konvertiert werden.
Wie oben schon erläutert, verwendet der 24bit Modus 16 Millionen Farben und 16bit im 565 Modus 65.536 Farben, bzw. "nur" 32.748 Farben im 555 Modus. Eine 24bit Farbe wird daher im 16bit Modus nie exakt die gleiche sein. Es wird zu der am nächsten liegenden Farbe konvertiert, deshalb sind Farbverläufe in 24bit viel weicher als im 16bit.
Wie kommt man aber nun zu einer 16bit Farbe, z.B. RGB(201, 134, 45) ein 24bit Farbanteil verwendet 8bit pro Anteil. Der Trick ist, die restlichen Bit die nicht benötigt werden, abzuschneiden. Im 555 Modus werden nur 5 Bit benötigt und die restlichen 3 Bit können abgeschnitten werden, und zwar bei jedem Farbanteil.
Farbe RGB-Wert Binärwert Binär (555) Binär (565)
rot 201 11001001 11001 11001
grün 134 10000110 10000 100001
blau 45 00101101 00101 00101
Im 565 Modus werden bei Rot und Blau 3 Bit abgeschnitten, und nur bei Grün 2 Bit.
Und nun zum Code, als Erstes werden die Farbanteile Rot, Grün und Blau aus der 24bit Farbe extrahiert.
 
Public Function GetRGB24(Color As Long) As cRGB
  Dim bsG As Single
  Dim bsR As Single
  Dim bsB As Single
  Dim Rmask As Long
  Dim Gmask As Long
  Dim Bmask As Long

   'Wenn wir im 16bit Modus sind können wir
   'die 24bit Farbmasken nicht auslesen.
   'Sie müssen daher per Hand deklariert werden.
  bsR = 1
  bsG = &H100
  bsB = &H10000
    
  Rmask = &HFF
  Gmask = 65280 '&HFF00 wird von VB nicht richtig gesetzt
  Bmask = &HFF0000

  With GetRGB24
     .r = (Color And Rmask) / bsR
     .g = (Color And Gmask) / bsG
     .b = (Color And Bmask) / bsB
  End With
    
End Function
 
Nachfolgend die eigentliche Konvertierung:
 
Function To16bit(Color As cRGB) As Long
  Dim bsG As Single
  Dim bsR As Single
  Dim bsB As Single
  Dim cr As cRGB

  cr = Color
   'Wir scheiden 3 bits vom Rot
   'Dazu benutzen wir das "\" symbol
   '(integer division) anstatt "/" welches
   'keinen Integer-Wert zurückliefert was
   'wiederum falsche Ergebnise bringen würde

  cr.r = cr.r \ &H8
    
   'Prüfen welchen 16bit Modus wir haben
   'bei 555 schneiden wir 3 bits, bei 565 2 bits
  If Mode16 = Mode555 Then
     cr.g = cr.g \ &H8
  Else
     cr.g = cr.g \ &H4
  End If

   '3 bits von Blau abschneiden
  cr.b = cr.b \ &H8

   'bsR, bsG und bsB beinhalten die Anzahl
   'abzuschneidender Bits ("shifting")
   'die Grüne shifting-Variable ist bei
   'Modus 555 und 565 gleich
  bsG = &H20

  If Mode16 = Mode555 Then
    If RGBForm = C_BGR Then
       'ist die Reihenfolge BGR hat Rot den
       'höchsten und Blau den niedrigsten Wert
       'im 555 Modus müssen 10 bits abgeschnitten
       'werden um den Rotanteil zu erhalten
      bsR = &H400
      bsB = 1
    ElseIf RGBForm = C_RGB Then
       'bei RGB genau umgekehrt
      bsB = &H400
      bsR = 1
    End If
  ElseIf Mode16 = Mode565 Then
    If RGBForm = C_BGR Then
        'im 565 Modus sind es 11 bits
       bsR = &H800
       bsB = 1
    ElseIf RGBForm = C_RGB Then
       bsB = &H800
       bsR = 1
    End If
  End If

   'mit dem logischen Operator OR kombinieren
   'wir die einzelnen Farben zu einem Farbwert
  To16bit = cr.r * bsR Or cr.g * bsG Or cr.b * bsB

End Function
 
Ein Beispiel zur Konvertierung von 24bit zu 16bit Farben mit DirectDraw können Sie hier downloaden. Dieses Beispiel finden sie auch mit ausführlicher Code-Abbildung in unserer Tipp-Rubrik.

Download  (5,6 kB) Downloads bisher: [ 1257 ]

Zum Seitenanfang

Startseite | VB-/VBA-Tipps | Projekte | Tutorials | API-Referenz | Komponenten | Bücherecke | Gewinnspiele | VB-/VBA-Forum | DirectX-Forum | VB.Net | .Net-Forum | Foren-Archiv | Chat | Spielplatz | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report
Letzte Aktualisierung: Samstag, 05. November 2005