Referenz - Interne Pixelformate
Die Darstellung von Grafiken im Speicher mit FreeBASIC lässt sich in mehrere Kapitel untergliedern:
- Darstellung eines einzelnen Pixels
- Transparenz - Maskenfarben und Alpha-Wert
- Struktur eines Bildpuffers für die Drawing Primitives
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 |
---|---|
1 | 1 Byte pro Pixel, indiziert, Index von 0 bis 1. |
2 | 1 Byte pro Pixel, indiziert, Index von 0 bis 3. |
4 | 1 Byte pro Pixel, indiziert, Index von 0 bis 15. |
8 | 1 Byte pro Pixel, indiziert, Index von 0 bis 255. |
15, 16 | 2 Bytes pro Pixel, Direct Color, Format &bRRRRRGGGGGGBBBBB. |
24, 32 | 4 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 24bpp 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 der 15bpp- und der 16bpp-Modus dasselbe 16bpp-Format. Ähnliches gilt den 24bpp- und den 32bpp-Modus 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:
Byte | Komponente |
---|---|
0 | Blau |
1 | Grün |
2 | Rot |
3 | Alpha |
Transparenz - Maskenfarben und Alpha-Wert
Maskenfarbe
Die Maskenfarbe hängt von der aktuellen Farbtiefe ab; sie ist einer von diesen Werten:
Farbtiefe | Maskenfarbnummer |
---|---|
1, 2, 4, 8 | 0 |
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 Alphawert im Bereich von 0 bis 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 ULong, Byval dst _
As ULong, Byval param As Any Ptr ) As ULong
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 Alphawert für alle Pixel
Else
a = color_src[3]
' aus jedem Pixel den Alphawert 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 der Methode CUSTOM benutzt werden kann (siehe PUT (Grafik), DRAW STRING).
- 'src' entspricht der Farbinformation des überzeichnenden (neuen) Pixels.
- 'dst' entspricht der Farbinformation des zu überzeichnenden (bereits auf der Zeichenfläche befindlichen) Pixels.
- 'param' ist ein Pointer auf einen UBYTE-Wert zwischen 0 und 255, der den Alpha-Wert darstellt.
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 mit allen Drawing Primitives (z. B. PSET, LINE (Grafik)) kompatibel. Siehe auch PUT (Grafik), GET (Grafik).
Version 2 (aktuell)
Version 2 des Bildpuffers ist seit v0.17 verfügbar; das Programm kann ohne Kommandozeilenoption oder mit -lang fb compiliert 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 ULONG
END UNION
bpp AS LONG
width AS ULONG
height AS ULONG
pitch AS ULONG
_reserved(1 to 12) AS UBYTE
END TYPE
Wie Sie sehen, enthält der Header noch die Informationen des alten Formates. Dadurch ist eine schnelle und einfache Prüfung der Pufferversion möglich, wodurch die Abwärtskompatibilität gewahrt wird. Die einzelnen Elemente bedeuten in der Deklaration bedeuten Folgendes:
- 'old' ist eine Speicherstruktur, die dem Header der Version 1 entspricht.
- 'type' ist ein LONG-Wert, der die Version des Headers angibt. Da es sich in einem UNION mit 'old' befindet, wird es dieses Attribut überschreiben. Soll die neue Header-Struktur benutzt werden, so hat 'type' den Wert 7. Diesen Wert kann der alte Header nie erreichen, wodurch sicher gestellt ist, dass die Version eindeutig erkennbar ist.
- 'bpp' gibt die Farbtiefe in Bytes pro Pixel an. Dieser Wert ist entweder gleich 1 (für 1-8bit), 2 (für 15bit und 16bit), oder gleich 4 (für 24bit und 32bit)
- 'width' und 'height' geben die Breite und Höhe des Bildpuffers in Pixeln an.
- 'pitch' gibt die Anzahl der Bytes pro Zeile an. Der Wert berechnet sich auch für alle Farbtiefen (bpp = 1, 2 oder 4): pitch=(width*bpp+15) AND -16. Jede Bildzeile wird also so aufbereitet, dass die Anzahl der Byte pro Zeile ein Vielfaches von 16 ist. Dies wirkt sich darin aus, dass in fast jedem Bildpuffer einige ungenutzte Bytes vorhanden sind; in Anbetracht der Größe des zur Verfügung stehenden Speichers fallen diese aber gar nicht ins Gewicht. Der Vorteil dieser Methode liegt darin, dass sich der Umgang mit den Bildpuffern optimieren lässt; die heutigen 32bit-Prozessoren bieten 128-Bit-Register (= 16 Byte) die damit optimal angewandt werden können. Einem minimal größeren Speicheraufwand steht also ein spürbar kleinerer Zeitaufwand bei Berechnungen gegenüber.
- '_reserved' ist eine 12 Byte große Speicherstelle, die später möglicherweise für verschiedene OpenGL-Operationen verwendet werden kann; die Verwendung dieser Stellen steht im Moment noch nicht fest. Der Programmierer kann frei entscheiden, ob er diese Speicherstellen nutzen möchte, sollte dabei aber bedenken, dass sein Programm damit möglicherweise später nicht mehr kompatibel ist.
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 Speicherbereichs auf den entsprechenden Stellen angesammelt hat. Durch CALLOCATE wird immer ein null-initialisierter Speicherbereich reserviert.
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. IMAGECREATE arbeitet im Prinzip genauso wie ALLOCATE, berechnet jedoch selbständig den zu reservierenden Speicherplatz und legt außerdem bereits den Header an. Daher handelt es sich hier um die bevorzugte Vorgehensweise für Bildpuffer. 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.
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)
- 'p' ist ein Pointer, der auf den gewünschten Pixel zeigt. Benutzen Sie bevorzugt einen UBYTE PTR, damit bei der Berechnung des Formelausdrucks kein Fehler auftritt. Wird z. B. an einem INTEGER PTR die Operation +=1 ausgeführt, so erhöht sich sein Wert tatsächlich um vier bzw. acht, da ein INTEGER je nach Plattform die Länge von 4 bzw. 8 Byte besitzt.
- 'buffer' gibt die Startadresse des Bildpuffers an.
- 'x' und 'y' sind die Koordinaten des Pixels, relativ zum linken oberen Rand des Bildpuffers.
- 'w' ist die Breite des Bildes in Pixeln.
- 'bpp' gibt die Farbtiefe des Bildpuffers in Bytes pro Pixel an.
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:
Byte | Bedeutung | Wert |
---|---|---|
0 bis 3 | Version des Bildpuffers - neuer Header | 7 |
4 bis 7 | Farbtiefe in bpp | 4 |
8 bis 11 | Breite des Bildpuffers in Pixeln | 2 |
12 bis 15 | Höhe des Bildpuffers in Pixeln | 2 |
16 bis 19 | Bytes pro Zeile in diesem Puffer | 16 |
20 bis 31 | zwölf reservierte Bytes, die in zukünftigen OpenGL-Anwendungen eine Bestimmung finden könnten | 0 |
32 bis 35 | Farbe des ersten Pixels (links oben) im Format &hAARRGGBB | &h00FF0000 |
36 bis 39 | Farbe des zweiten Pixels (rechts oben) im Format &hAARRGGBB | &h0000FF00 |
40 bis 43 | Padding-Stelle - ohne Bedeutung | &hFFFF00FF |
44 bis 47 | Padding-Stelle - ohne Bedeutung | &hFFFF00FF |
48 bis 51 | Farbe des dritten Pixels (links unten) im Format &hAARRGGBB | &h00FFFF00 |
52 bis 55 | Farbe des vierten Pixels (rechts unten) im Format &hAARRGGBB | &h000000FF |
56 bis 59 | Padding-Stelle - ohne Bedeutung | &hFFFF00FF |
60 bis 63 | Padding-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), &hFFFF00
Pset buffer, (1, 1), &h0000FF
With *header
PRINT "Header des Buffers:"
PRINT "Version ID: "; .type
PRINT "Bytes pro Pixel: "; .bpp
PRINT "Breite: "; .width
PRINT "Hoehe: "; .height
PRINT "Bytes pro Zeile: "; .pitch
End With
Draw String (0, 100), "Die Grafik:"
Put (100, 100), buffer, PSET
Open Err For Output As #1
PRINT #1, "Pixeldaten:"
PRINT #1, ""
For i = 0 To 31
PRINT #1, i, Hex(Colors[i], 2),
If (i + 1) Mod 4 = 0 Then PRINT #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:
Über das UDT Image kann auch direkt auf die Header-Daten zugegriffen werden:
#Include "fbgfx.bi"
ScreenRes 400, 300, 32
Dim As FB.Image Ptr img
Dim As UInteger Ptr Pixel
Dim As UInteger x, y, pitch
img = ImageCreate(256, 256)
pitch = img->pitch
For y = 0 To 255
For x = 0 To 255
Pixel = Cast(Any Ptr, img) + 32 + (y * pitch) + (x * 4)
*Pixel = RGB(x, y, 0)
Next
Next
Put (0, 0), img
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:
- Bedingungen für Version 1:
FreeBASIC-Version <= 0.16 oder
FreeBASIC-Version >= 0.17 und Kommandozeilenoption -lang qb oder
FreeBASIC-Version >= 0.17 und Kommandozeilenoption -lang deprecated - Bedingungen für Version 2:
FreeBASIC-Version >= 0.17 und Kommandozeilenoption -lang fb
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 Programierer muss selbständig Daten des jüngeren Formats umwandeln. Bei direkten Speicherzugriffen ohne die FB-eigenen Drawing Primitives kann anhand der ersten LONG-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(LONG, 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)
Die alte Version des Bildpuffers entspricht dem Pufferformat von QB. Sie wurde bis FreeBASIC v0.16 benutzt; seit v0.17 ist sie nur noch dann verfügbar, wenn die Kommandozeilenoption -lang deprecated oder -lang qb eingesetzt wird. Der Bildpuffer besteht aus dem Header und den eigentlichen Pixelinformationen. Der Header ist ein Bitfeld (siehe TYPE (UDT) 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 Byte oder einer LONG-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:
- 'bpp' gibt die Farbtiefe in Bytes pro Pixel an. Er ist entweder gleich 1 (für 1-8bit), 2 (für 15bit und 16bit), oder gleich 4 (für 24bit und 32bit)
- 'width' gibt die Breite des Bildes in Pixeln an. Da 13 Bits verwendet werden, kann dieser Wert zwischen 1 und 8191 liegen; eine Breite von 0 ist (aus praktischen Gründen) ungültig.
- 'height' gibt die Höhe des Bildes in Pixeln an. Da hier eine volle USHORT-Stelle (16 Bit) verwendet wird, kann dieser Wert zwischen 1 und 65535 liegen; eine Höhe von 0 ist (aus praktischen Gründen) ungültig.
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:
Byte | Bedeutung | Wert |
---|---|---|
0 und 1 | (Breite SHL 3) OR bpp | 20 |
2 und 3 | Höhe | 2 |
4 bis 7 | Farbe des ersten Pixels (links oben) im Format &hAARRGGBB | &h00FF0000 |
8 bis 11 | Farbe des zweiten Pixels (rechts oben) im Format &hAARRGGBB | &h0000FF00 |
12 bis 15 | Farbe des dritten Pixels (links unten) im Format &hAARRGGBB | &h00FFFF00 |
16 bis 19 | Farbe des vierten Pixels (rechts unten) im Format &hAARRGGBB | &h000000FF |
Dies verdeutlicht auch dieses Programm:
#lang "deprecated"
ScreenRes 400, 300, 32
Dim x As UByte Ptr
Dim y As ULong Ptr
Dim i As Integer
x = ImageCreate(2, 2)
y = Cast(ULong Ptr, 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
Zusätzliche Informationen und Funktionen | ||||
---|---|---|---|---|
|