Interne Pixelformate

FreeBASIC-Referenz » Die Gfxlib » Interne Pixelformate

Die Darstellung von Grafiken im Speicher mit FreeBASIC lässt sich in mehrere Kapitel untergliedern:

Darstellung eines einzelnen Pixels
Die GFXLib verwendet immer eines von drei Pixelformaten: indizierte 1-Byte-Pixel, Direct Color 2-Bytes-Pixel (Hicolor) und Direct Color 4-Bytes-Pixel (Truecolor). Diese Formate werden in den entsprechenden Modi verwendet:

Farbtiefe (bpp)Pixelelformat
11 Byte pro Pixel, indiziert, Index zwischen 0 und 1.
21 Byte pro Pixel, indiziert, Index zwischen 0 und 3.
41 Byte pro Pixel, indiziert, Index zwischen 0 und 15.
81 Byte pro Pixel, indiziert, Index zwischen 0 und 255.
15, 162 Bytes pro Pixel, Direct Color, Format &bRRRRRGGGGGGBBBBB.
24, 324 Bytes pro Pixel, Direct Color, Format &bAAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB.

Indiziert bedeutet hierbei, dass jeder Zahl eine Farbe zugeordnet ist; beispielsweise kann ein Pixel mit dem Farbattribut 4 rot sein. Welche Farbe welcher Zahl zugeordnet ist, hängt vom Bildschirmmodus ab; siehe dazu die Standard-Paletten. Diese können mit PALETTE noch im Programmverlauf bearbeitet werden. Bei Direct-Color-Modi stellt die Zahl bereits die Farbe dar. Jeweils ein bestimmter Teil der Zahl stellt den Rot- Grün- und Blau-Anteil der Farbe dar. Bei der Angabe des Formats in obiger Tabelle steht jeweils ein Zeichen für ein Bit der Farbinformation. R symbolisiert jeweils den Rot-, G den Grün- und B den Blauanteil. Die Stellen A, die bei 24 und 32bpp aufgeführt sind, können tatsächlich nur in 32bpp-Modi genutzt werden. Sie stellen die Alpha- oder Transparenz-Komponente dar. In 24bpp-Modi sind diese Stellen jeweils mit null besetzt, sie werden lediglich zur leichteren Verwaltbarkeit beibehalten. Wie Sie sehen, verwenden die 15 und der 16bpp-Modi dasselbe 16bpp-Format. Dasselbe kann man über die 24 und 32bpp-Modi sagen, beide benutzen das 32bpp-Format. Das interne Format ist plattformunabhängig. Der Bildschirmspeicher wird in einem der drei Formate gehalten. Denken Sie daran, wenn Sie per SCREENPTR darauf zugreifen; dasselbe gilt für Daten, die in GET- und PUT-Feldern gespeichert sind. Mehr dazu unter Struktur eines Bildpuffers für die Drawing Primitives. In Modi mit mehr als einem Byte pro Pixel werden die Bytes in umgekehrter Reihenfolge im Speicher abgelegt; das obere Byte des Gesamtwertes ist also das zweite im Speicher, das untere Byte wird zuerst abgelegt. Befindet sich an der angenommenen Adresse 0 also eine 32bit-Pixelinformation, so werden ihre Komponenten folgendermaßen zerlegt:

ByteKomponente
0Blau
1Grün
2Rot
3Alpha

Transparenz - Maskenfarben und Alpha-Wert
Maskenfarbe
Die Maskenfarbe hängt von der aktuellen Farbtiefe ab; sie ist einer von diesen Werten:

FarbtiefeMaskenfarbnummer
1, 2, 4, 80
15, 16&hF81F
24, 32&hFF00FF


Indizierte Modi benutzen immer den Index 0 als Transparenzmaske, während Direct Color-Modi immer Pink verwenden.

Alphawert
FreeBASIC unterstützt Transparenzeffekte nach der ALPHA-Methode. Dabei wird neben dem Farb-Tripplet (Rot, Grün, Blau) auch ein Transparenzgrad, der Alphawert angegeben. Ebenso wie die Werte des Farbtripplets liegt auch der Alpha-Wert zwischen 0 und 255; dabei entspricht Alpha=0 völliger Transparenz und Alpha=255 völliger Überdeckung. Für alle Werte dazwischen werden 'durchscheinende' Bilder erzeugt, d.h. ein Farbwert wird berechnet, der 'zwischen' überzeichnender und überzeichneter Farbe liegt. Dabei gibt der Alphawert an, welcher der beiden Werte den Farbeindruck dominiert. Berechnet wird die neue Farbe nach dieser Funktion:

Function custom_alpha(Byval src As Uinteger, Byval dst _
   As Uinteger, Byval param As Any Ptr ) As Uinteger
    Dim As Ubyte ptr color_src, color_dst
    Dim As Ubyte     r, g, b, a

    color_src = Cast(Ubyte ptr, @src)
    color_dst = Cast(Ubyte ptr, @dst)

    If param <> 0 Then
        a = *Cast(Ubyte ptr, param)
        'ein alpha wert für alle pixel
    Else
        a = color_src[3]
        'aus jedem Pixel den alpha wert lesen
    End If

    r=(color_src[2]*a+color_dst[2]*(255-a))\255
    g=(color_src[1]*a+color_dst[1]*(255-a))\255
    b=( color_src[0]*a+color_dst[0]*(255-a))\255

    Return RGBA(r, g, b, 255)
End Function

(Diese Funktion ist so gehalten, dass sie mit PUT (Grafik) benutzt werden kann.)

Struktur eines Bildpuffers für die Drawing Primitives
Ein Bildpuffer ist ein Speicherbereich, in dem größere Blocks von Pixeldaten - kurz Bildausschnitte - abgelegt werden können. Dies kann ein Array sein (Siehe DIM, REDIM), oder ein mit ALLOCATE, CALLOCATE oder (besonders zu empfehlen) IMAGECREATE reservierter Bereich. In FreeBASIC werden zwei verschiedene Formate für Bildpuffer verwendet; abhängig von der Version des Compilers und den Kommandozeilenoptionen verwendet FreeBASIC Version 1 oder Version 2 der verfügbaren Formate. Beide Formate sind (ab FreeBASIC v0.17) mit allen Drawing Primitives kompatibel; siehe PSET, IMAGECREATE, GET (Grafik).

Version 2 (aktuell)
Diese Version des Bildpuffers ist seit v0.17 verfügbar; das Programm kann ohne oder mit der Kommandozeilenoption -lang fb kompiliert werden. Ebenso wie in der weiter unten aufgeführten Version 1 lässt sich dieses Bildpuffer-Format in einen Header und die eigentlichen Pixeldaten zerlegen. Der Bildpuffer ist dabei so angelegt:

TYPE Image FIELD = 1
UNION
old AS _OLD_HEADER
type AS UINTEGER
END UNION
bpp AS INTEGER
width AS UINTEGER
height AS UINTEGER
pitch AS UINTEGER
_reserved(1 to 12) AS UBYTE
END TYPE

Wie Sie sehen, enthält dieser Header noch die Informationen des alten Formates. Dadurch ist eine schnelle und einfache Prüfung des Bildformates möglich, wodurch die Abwärtskompatibilität gewahrt wird. Zur Überprüfung der Pufferversion siehe später. Die einzelnen Elemente bedeuten dabei Folgendes:

Wie bei 'pitch' bereits angesprochen, sind im neuen Datenformat die Pixel nicht mehr direkt aneinander gereiht, sondern Zeilenweise in Datenblocks zusammengefasst. Hinter jeder Zeile werden eine Reihe von Bytes reserviert, die nicht genutzt werden. Durch dieses 'Padding' können bestimmte Berechnungen schneller durchgeführt werden. Der Wert dieser Padding-Bytes ist abhängig von der Art der Erstellung des Puffers. Bei der Verwendung eines Arrays wird hier in den meisten Fällen null eingetragen, da mit DIM und REDIM initiierte Variablen i.d.R. mit null initialisiert werden. Eine Ausnahme sind Arrays, die durch = ANY initialisiert wurden; da hier der Inhalt des Speicherbereichs nicht gelöscht wird, und nur die tatsächlich genutzen Bytes im Laufe der Bildbearbeitung überschrieben werden, findet man hier immer den 'Datenmüll', der sich vor der Reservierung des Speicherberichs auf den entsprechenden Stellen angesammelt hat. Durch CALLOCATE wird immer ein null-initialisierter Speicherbereich reserviert. Da die Speicherbereiche, die mit IMAGECREATE reserviert wurden, werden vor Benutzung komplett mit einem Wert befüllt; dieser hängt von den Parametern, mit denen IMAGECREATE genutzt wird, und dem aktuellen Bildschirmmodus ab. Siehe IMAGECREATE für Details. Wie schon bei Arrays, die durch = ANY initialisiert wurden, findet man auch bei der Reservierung eines Speicherbereichs mittels ALLOCATE Datenmüll in den Padding-Bytes. Um nun bei gegebener Startadresse des Bildpuffers die Adresse eines einzelnen Pixels zu ermitteln, bedient man sich dieser Formel:

p = buffer+32+(y*((w*bpp+15) AND -16)+(x*bpp)

Dabei ist...

Steht 'pitch' aus dem Bildheader zur Verfügung, so kann der Ausdruck auch vereinfacht werden:

p = buffer + 32 + (y * pitch) + (x * bpp)

Der Speicherbedarf berechnet sich also nach dieser Formel:

size = 32 + ( h * (( w * bpp + 15 ) AND -16 )

Beispiel: Ein Bildpuffer der Größe 2x2 Pixel in einem 32bpp-Modus, der einen roten, grünen, gelben und blauen Pixel enthält:

ByteBedeutungWert
0 bis 3Version des Bildpuffers - neuer Header7
4 bis 7Farbtiefe in bpp4
8 bis 11Breite des Bildpuffers in Pixeln2
12 bis 15Höhe des Bildpuffers in Pixeln2
16 bis 19Bytes pro Zeile in diesem Puffer16
20 bis 31zwölf reservierte Bytes, die in zukünftigen OpenGL-Anwendungen eine Bestimmung finden könnten0
32 bis 35Farbe des ersten Pixels (links oben) im Format ARGB&H00FF0000
36 bis 39Farbe des zweiten Pixels (rechts oben) im Format ARGB&H0000FF00
40 bis 43Padding-Stelle - ohne Bedeutung&HFFFF00FF
44 bis 47Padding-Stelle - ohne Bedeutung&HFFFF00FF
48 bis 51Farbe des dritten Pixels (links unten) im Format ARGB&H000000FF
52 bis 55Farbe des vierten Pixels (rechts unten) im Format ARGB&H00FF00FF
56 bis 59Padding-Stelle - ohne Bedeutung&HFFFF00FF
60 bis 63Padding-Stelle - ohne Bedeutung&HFFFF00FF

Beispiel 1:

#Include "fbgfx.bi"
Using FB

ScreenRes 400, 300, 32

Dim buffer As Any      Ptr
Dim header As Image    Ptr
Dim Pixels As UInteger Ptr
Dim Colors As UByte    Ptr
Dim i      As UInteger

buffer = ImageCreate(2, 2)
header = buffer
Pixels = buffer + 32
Colors = buffer + 32

Pset buffer, (0, 0), &hFF0000
Pset buffer, (1, 0), &h00FF00
Pset buffer, (0, 1), &H0000FF
Pset buffer, (1, 1), &HFFFF00

With *header
   PRINT "Header of the Buffer:"
   PRINT "Version ID:      "; .type
   PRINT "Bytes per Pixel: "; .bpp
   PRINT "Width:           "; .width
   PRINT "Height:          "; .height
   PRINT "Bytes per Row:   "; .pitch
End With

Draw String (0, 100), "The graphic:"
Put (100, 100), buffer, PSET

Open Err For Output As #1
PRINT #1, "Pixel Data:"
PRINT #1, ""
For i = 0 To 31
   PRINT #1, i, Hex(Colors[i], 2),
   If (i + 1) Mod 4 = 0 Then ? #1, ""
   If (i    ) Mod 4 = 0 Then
      PRINT #1, Hex(Pixels[i \ 4], 8)
   Else
      PRINT #1, ""
   End If
Next

ImageDestroy buffer
Close #1

Sleep

Beispiel 2:

#Include "fbgfx.bi"
Using FB

ScreenRes 400, 300, 32

Dim As Any      Ptr buffer
Dim As Image    Ptr header
Dim As UInteger Ptr Pixel
Dim As UInteger     x, y, pitch

buffer = ImageCreate(256, 256)
header = buffer

pitch = header->pitch

'Open Err For Output As #1
'Print #1, Using  "Start Adress: ##########"; Cast(Integer, buffer)
For    y = 0 To 255
   For x = 0 To 255
       Pixel = buffer + 32 + (y * pitch) + (x * 4)
      *Pixel = RGB(x, y, 0)
   Next
   'Print #1, Using "Line ### starts at: ##########"; y; Cast(Integer, Pixel)
Next

Put (0, 0), buffer
'Close #1

Sleep

Erkennen des Puffertyps und Bearbeitung
Je nach gewählten Ausgangsbedingungen wird FreeBASIC bei der Erstellung neuer Datenpuffer immer eine bestimmte Version des Puffers benutzen:

Dennoch ist es möglich, dass ein anderes Pufferformat behandelt werden soll, als das unter gegebenen Bedingungen von FreeBASIC gewählte, z.B. wenn mittels BLOAD Daten in den Speicher geladen werden, die ein älteres Programm erstellt hat. Die Drawing Primitives ab FreeBASIC v0.17 können - bei jeder gewählten Kommandozeilenoption - mit beiden Pufferformaten umgehen; sie erkennen automatisch, welche Struktur angewandt werden muss. Die Drawing Primitives von FreeBASIC-Versionen vor v0.17 können nur mit Version 1 des Pufferformats umgehen; der User muss selbstständig Daten des jüngeren Formats umwandeln. Bei direkten Speicherzugriffen ohne die FB-Eigenen Drawing Primitives kann anhand des ersten INTEGER-Stelle des Headers überprüft werden, ob es sich um das alte oder das neue Format handelt: Beim neuen Format beinhaltet diese immer den Wert 7; beim alten Format ist hier ein anderer Wert gespeichert, der sich aus Farbtiefe, Höhe und Breite des Bildpuffers zusammensetzt. Dieser Wert kann nie gleich sieben werden. Mit dem Headertyp ändert sich auch der Startpunkt der eigentlichen Pixeldaten. Diese Formel gibt unabhängig von Pointertyp und Pufferformat den richtigen Startpunkt aus:

start = buffer + IIF( PEEK(INTEGER, buffer) = 7, 32, 4 ) \ SIZEOF(buffer)

Beachten Sie hierbei, dass Sie separat prüfen müssen, ob die Zeilen gepaddet sind. Dies ist bei Version 2 IMMER der Fall, in Version 1 hingegen NIE.

Version 1 (veraltet)
Diese Version des Bildpuffers wurde bis FreeBASIC v0.16 benutzt; seit v0.17 ist er nur noch dann verfügbar, wenn die Kommandozeilenoption -lang deprecated oder -lang qb
eingesetzt wird. Er besteht aus dem Header und den eigentlichen Pixelinformationen. Der Header ist ein Bitfeld (siehe TYPE (UDTs) und Bitfelder), das Angaben über Höhe, Breite und Farbtiefe des Bildes enthält; an ihn schließen sich die eigentlichen Pixeldaten im oben genannten Format an. Der Header besteht insgesamt aus 32bit, also vier Bytes oder einer INTEGER-Stelle. Er ist folgendermaßen aufgebaut:

TYPE _OLD_HEADER FIELD = 1
bpp : 3 AS USHORT
width : 13 AS USHORT
height AS USHORT
END TYPE

Die einzelnen Elemente bedeuten dabei Folgendes:

Dem Header schließen sich die eigentlichen Pixeldaten an; für jeden Pixel werden dabei so viele Bytes verwendet, wie in 'bpp' angegeben ist. Die Pixeldaten sind von links nach rechts und von oben nach unten geordnet. Die Größe eines Puffers nach diesem Format lässt sich also folgendermaßen berechnen:

size = 4 + (w * h * b)

Wobei 'w' die Breite, 'h' die Höhe und 'b' die Farbtiefe in Bytes pro Pixel ist.
Beispiel: Ein Bildpuffer der Größe 2x2 Pixel in einem 32bpp-Modus, der einen roten, grünen, gelben und blauen Pixel enthält:

ByteBedeutungWert
0 und 1(Breite SHL 3) OR bpp33
2 und 3Höhe1
4 bis 7Farbe des ersten Pixels (links oben) im Format ARGB&H00FF0000
8 bis 11Farbe des zweiten Pixels (rechts oben) im Format ARGB&H0000FF00
12 bis 15Farbe des dritten Pixels (links unten) im Format ARGB&H00FFFF00
16 bis 19Farbe des vierten Pixels (rechts unten) im Format ARGB&H000000FF

Dies verdeutlicht auch dieses Programm:

ScreenRes 400, 300
Dim x As UByte    Ptr
Dim y As UInteger Ptr
Dim i As Integer
x = ImageCreate(2, 2)
y = x
Pset x, (0, 0), &HFF0000
Pset x, (1, 0), &H00FF00
Pset x, (0, 1), &H0000FF
Pset x, (1, 1), &HFFFF00
For i = 0 To 19
   Print i, x[i],
   If ( i      Mod 4) = 0 Then
      Print Hex(y[i \ 4], 8)
   Else
      Print
   End If
   If ((i + 1) Mod 4) = 0 Then Print
Next
ImageDestroy x
Sleep