Achtung: Dieser Abschnitt richtet sich an fortgeschrittene Programmierer, die einen tieferen Einblick in die Verwaltung von Grafikpuffern erhalten wollen. Er behandelt folgende Themen:
- Double Buffering
- Arten der Bildpuffer
- Bildpuffer bei Drawing Primitives
- GfxPrint
- LZW-Codec in der GfxLib
- OpenGL-Textur
Double Buffering
Vermeiden Sie Double Buffering (ein Bild zuerst in einen Puffer schreiben, und es dann in den Video-RAM kopieren) soweit wie möglich. Die Gfx-Lib benutzt bereits ein Double-Buffering-System, um den Bildschirm zu aktualisieren. Wenn Sie Ihren eigenen Zweitpuffer verwenden, müssen nur weitere (unnötige) Bildschirmkopien durchgeführt werden, wodurch Ihr Programm langsamer wird. Stattdessen sollten Sie die bereits unterstützten Flipping-Funktionen (Verwendung mehrerer Bildschirmseiten; siehe SCREENSET und das Beispiel von MULTIKEY) benutzen.
Arten der Bildpuffer
GET und PUT unterstützen neben normalen Arrays auch Pointer als Bildpuffer:
TYPE FB_IMAGE FIELD = 1
width AS USHORT
height AS USHORT
imageData(64000) AS UBYTE
END TYPE
DIM udt AS FB_IMAGE
DIM udt_ptr AS FB_IMAGE PTR
DIM array(16008) AS INTEGER
DIM array_ptr AS INTEGER PTR
udt_ptr = @udt
array_ptr = @array(0)
SCREENRES 320, 200
' Diese Anweisungen haben dieselbe Funktion
GET (0, 0)-(319, 199), array
GET (0, 0)-(319, 199), array(0)
GET (0, 0)-(319, 199), @array(0)
GET (0, 0)-(319, 199), array_ptr
GET (0, 0)-(319, 199), @array_ptr[0]
' Diese Anweisungen sind ebenfalls äquivalent zueinander
GET (0, 0)-(319, 199), @udt
GET (0, 0)-(319, 199), udt_ptr
GET (0, 0)-(319, 199), @udt_ptr[0]
SLEEP
In diesem Beispiel wurde GET verwendet, es funktioniert aber ebenso mit PUT.
Bildpuffer bei Drawing Primitives
Alle Drawing Primitives (einfachste Grafikanweisungen wie LINE, CIRCLE usw.) können an Grafikpuffer angewandt werden. Dadurch ist es für Sie möglich, auf eine beliebige Anzahl von nicht-Bildschirm-Oberflächen zu zeichnen, und diese später auf den Bildschirm zu übertragen:
DIM buffer(9602) AS USHORT
DIM sprite(1028) AS USHORT
SCREENRES 320, 200
' Da als Puffer ein GET/PUT-Puffer angenommen wird,
' setzen wir seine ersten vier Bytes so, dass sie einen
' normalen GET/PUT-Puffer-Header bilden.
buffer(0) = 160 SHL 3 ' Breite * 8
buffer(1) = 120 ' Höhe
CIRCLE buffer, (16, 16), 15, 12, , , 1, F
GET buffer, ( 0, 0)-( 31, 31), sprite
PSET sprite, (16, 16), 15
LINE buffer, ( 0, 0)-(159, 119), 2, B
PUT buffer, (50, 50), sprite, PSET
PUT (80, 40), buffer, PSET
SLEEP
Einen einfacheren Weg, einen solchen Grafikpuffer zu erstellen stellt die IMAGECREATE-Funktion dar. Durch sie wird automatisch ein Puffer reserviert und initialisiert. Das obige Beispiel könnte dadurch so aussehen:
DIM buffer AS ANY PTR
DIM sprite AS ANY PTR
SCREENRES 320, 200
buffer = IMAGECREATE(160, 120)
sprite = IMAGECREATE(32, 32)
CIRCLE buffer, (16, 16), 15, 12, , , 1, F
GET buffer, ( 0, 0)-( 31, 31), sprite
PSET sprite, (16, 16), 15
LINE buffer, ( 0, 0)-(159, 119), 2, B
PUT buffer, (50, 50), sprite, PSET
PUT (80, 40), buffer, PSET
IMAGEDESTROY buffer
IMAGEDESTROY sprite
SLEEP
Wenn Sie auf einen Puffer zeichnen, werden die Koordinaten durch den letzten Aufruf von WINDOW beeinflusst, jedoch nicht von VIEW. Die Clipping-Grenzen werden auf die Gesamtgröße des Puffers gesetzt. Natürlich kann der optionale Zielpuffer-Parameter bei allen Drawing Primitives angegeben werden und darf sowohl ein Array als auch ein Pointer sein, wie im Falle des GET/PUT-Bildpuffers.
GfxPrint
Sie können die Funktion der PRINT-Routine durch eine eigene Prozedur erweitern, die die vordefinierten Gfxlib-fonts verwendet. Das folgende Beispiel enthält die SUB GfxPrint, die das Zeichnen von transparentem Text an jeder Position sowie Clipping-Grenzen unterstützt; ebenso ist es möglich, in einen Bildpuffer zu schreiben.
Anmerkung: Dieses Beispiel funktioniert nur mit FreeBASIC-Versionen <= v0.15
Ab FB-Version 0.16 gibt es die Funktion DRAW STRING. Für neuere FreeBASIC-Versionen gibt es die DrawString-Routine (Ersatz für GfxPrint)
DECLARE SUB GfxPrint( BYREF text AS STRING, _
BYVAL x AS INTEGER, BYVAL y AS INTEGER, _
BYVAL col AS INTEGER, BYVAL buffer AS ANY PTR = 0 )
TYPE fb_FontType
h AS INTEGER
data AS UBYTE PTR
END TYPE
' Entfernen Sie das Kommentar-Zeichen vor dem
' Zeichenformat, das Sie benutzen wollen
'EXTERN fb_FontData ALIAS "fb_font_8x8" AS fb_FontType
'EXTERN fb_FontData ALIAS "fb_font_8x14" AS fb_FontType
'EXTERN fb_FontData ALIAS "fb_font_8x16" AS fb_FontType
SUB GfxPrint( BYREF text AS STRING, _
BYVAL x AS INTEGER, BYVAL y AS INTEGER, _
BYVAL col AS INTEGER, BYVAL buffer AS ANY PTR = 0 )
DIM row AS INTEGER, i AS INTEGER
DIM bits AS UBYTE PTR
FOR i = 1 TO LEN(text)
bits = fb_FontData.data + (ASC(MID$(text, i, 1)) * _
fb_FontData.h)
FOR row = 0 TO fb_FontData.h-1
IF (buffer) THEN
LINE buffer, (x + 7, y + row)-(x, y + row), col, _
, *bits SHL 8
ELSE
LINE (x + 7, y + row)-(x, y + row), col, , *bits _
SHL 8
END IF
bits += 1
NEXT row
x += 8
NEXT i
END SUB
SCREENRES 320, 200
GfxPrint "Hello world!", 112, 96, 15
SLEEP
Achtung: Die GfxLib speichert alle Font- und Paletten-Daten in einem LZW-komprimierten Format, um Ihre EXEs klein zu halten, und dekomprimiert sie erst mit dem ersten SCREENRES-Aufruf. Wenn Sie SCREENRES in Ihrem Programm nie aufrufen, werden Zugriffe auf die Palette- und Font-Daten unbrauchbare Daten zurückliefern.
LZW-Codec in der GfxLib
Die GfxLib speichert die Paletten- und Font-Daten in einem LZW-komprimierten Format und entpackt diese erst, wenn zum ersten mal SCREENRES aufgerufen wird. Der LZW-Codec ist in Ihren Programmen nicht aufrufbar; er wurde nicht als eigener Befehl eingebaut. Es ist dennoch möglich, auf die Kompressionsfunktionen zuzugreifen, indem Sie die Funktionen einfach deklarieren; die GfxLib muss jedoch durch einen SCREENRES-Aufruf aktiviert sein.
Beispiel:
DECLARE FUNCTION LZW_Encode _
ALIAS "fb_hEncode" ( _
BYVAL in_buffer AS ANY PTR, _
BYVAL in_size AS INTEGER, _
BYVAL out_buffer AS ANY PTR, _
BYREF out_size AS INTEGER _
) AS INTEGER
DECLARE FUNCTION LZW_Decode _
ALIAS "fb_hDecode" ( _
BYVAL in_buffer AS ANY PTR, _
BYVAL in_size AS INTEGER, _
BYVAL out_buffer AS ANY PTR, _
BYREF out_size AS INTEGER _
) AS INTEGER
DIM src_buffer(100000) AS UBYTE
DIM src_size AS INTEGER
DIM dest_buffer(100000) AS UBYTE
DIM dest_size AS INTEGER
' funktioniert nur, wenn die GfxLib verwendet wird
SCREENRES 400, 300
src_size = 0
OPEN "gfxlib.txt" FOR BINARY AS #1
WHILE NOT EOF(1)
GET #1,, src_buffer(src_size)
src_size += 1
WEND
CLOSE #1
PRINT "Data size before compression:", src_size
GOSUB ShowData
dest_size = 100000
PRINT "Compressing...";
LZW_Encode @src_buffer(0), src_size, _
@dest_buffer(0), dest_size
PRINT "done."
PRINT "Data size after compression:", dest_size
PRINT
src_size = 100000
PRINT "Decompressing...";
LZW_Decode @dest_buffer(0), dest_size, _
@src_buffer(0), src_size
PRINT "done."
PRINT "Data size before decompression:", src_size
GOSUB ShowData
SLEEP
END
ShowData:
DIM i AS INTEGER
PRINT "Contents: ";
FOR i = 3 TO 36: PRINT CHR$(src_buffer(i)); : NEXT
PRINT " [...]"
RETURN
OpenGL-Textur
Das Erstellen einer OpenGL-Textur mit einem GET/PUT-Puffer ist einfach; hier ist ein kleiner Codeauszug, der diese Aufgabe erledigt:
#DEFINE TEX_MASKED &h1
#DEFINE TEX_MIPMAP &h2
#DEFINE TEX_NOFILTER &h4
#DEFINE TEX_HASALPHA &h8
FUNCTION CreateTexture( BYVAL buffer AS ANY PTR, _
BYVAL flags AS INTEGER = 0 ) AS GLuint
REDIM dat(0) AS UBYTE
DIM p AS UINTEGER PTR, s AS USHORT PTR
DIM AS INTEGER w, h, x, y, col
DIM tex AS GLuint
DIM AS GLenum format, minfilter, magfilter
CreateTexture = 0
s = buffer
w = s[0] SHR 3
h = s[1]
IF( (w < 64) OR (h < 64) ) THEN
EXIT FUNCTION
END IF
IF( (w AND (w-1)) OR (h AND (h-1)) ) THEN
'' Width/height not powers of 2
EXIT FUNCTION
END IF
REDIM dat(w * h * 4) AS UBYTE
p = @dat(0)
glGenTextures 1, @tex
glBindTexture GL_TEXTURE_2D, tex
FOR y = h-1 TO 0 STEP -1
FOR x = 0 TO w-1
col = POINT(x, y, buffer)
' Swap R and B so we can use the GL_RGBA texture format
col = RGBA(col AND &hFF, _
(col SHR 8) AND &hFF, _
(col SHR 16) AND &hFF, _
col SHR 24)
IF( flags AND TEX_HASALPHA ) THEN
*p = col
ELSE
IF( (flags AND TEX_MASKED) AND (col = &hFF00FF) ) THEN
*p = 0
ELSE
*p = col OR &hFF000000
END IF
END IF
p += 4
NEXT x
NEXT y
IF( flags AND ( TEX_MASKED OR TEX_HASALPHA ) ) THEN
format = GL_RGBA
ELSE
format = GL_RGB
END IF
IF( flags AND TEX_NOFILTER ) THEN
magfilter = GL_NEAREST
ELSE
magfilter = GL_LINEAR
END IF
IF( flags AND TEX_MIPMAP ) THEN
gluBuild2DMipmaps GL_TEXTURE_2D, format, w, _
h, GL_RGBA, GL_UNSIGNED_BYTE, @dat(0)
IF( flags AND TEX_NOFILTER ) THEN
minfilter = GL_NEAREST_MIPMAP_NEAREST
ELSE
minfilter = GL_LINEAR_MIPMAP_LINEAR
END IF
ELSE
glTexImage2D GL_TEXTURE_2D, 0, format, w, _
h, 0, GL_RGBA, GL_UNSIGNED_BYTE, @dat(0)
minfilter = magfilter
END IF
glTexParameteri GL_TEXTURE_2D, _
GL_TEXTURE_MIN_FILTER, minfilter
glTexParameteri GL_TEXTURE_2D, _
GL_TEXTURE_MAG_FILTER, magfilter
CreateTexture = tex
END FUNCTION