|
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.
|
|
|
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.
|
|
|
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.
|
|
|
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
]
|
|
|