Referenz - Tipps und Tricks
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 Grafikpuffer
- Grafikpuffer 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 Gfxlib 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 Grafikpuffer
GET und PUT unterstützen neben normalen Arrays auch Pointer als Grafikpuffer:
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.
Grafikpuffer 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.
Beispiel:
DIM buffer(9602) AS USHORT
DIM sprite(1028) AS USHORT
SCREENRES 320, 200
' Da als Puffer ein Grafikpuffer angenommen wird,
' setzen wir seine ersten vier Bytes so, dass sie einen
' normalen Grafikpuffer-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 Funktion IMAGECREATE dar. Durch sie wird automatisch ein Puffer der richtigen Größe reserviert und sein Header initialisiert. Das obige Beispiel könnte dadurch so aussehen:
Beispiel: Grafikpuffer mit IMAGECREATE reservieren
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. Der optionale Zielpuffer-Parameter kann bei allen Drawing Primitives angegeben werden und darf sowohl ein Array als auch ein Pointer sein, wie im Falle des Grafikpuffers.
GfxPrint
Um einen transparenten Text pixelgenau an einer beliebigen Fensterposition auszugeben, dient der Befehl DRAW STRING. Vor FreeBASIC v0.16 existierte dieser Befehl noch nicht, konnte jedoch durch eine eigene Routine ersetzt werden. 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 Grafikpuffer zu schreiben.
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
w As INTEGER
h AS INTEGER
data AS UBYTE PTR
END TYPE
ENUM
FB_FONT_8 = 0,
FB_FONT_14,
FB_FONT_16,
FB_FONT_COUNT
END ENUM
EXTERN __fb_font(0 to FB_FONT_COUNT-1) ALIAS "__fb_font" AS fb_FontType
' Entfernen Sie das Kommentar-Zeichen vor dem
' Zeichenformat, das Sie benutzen wollen
'#DEFINE fb_FontData (__fb_font(FB_FONT_8))
'#DEFINE fb_FontData (__fb_font(FB_FONT_14))
'#DEFINE fb_FontData (__fb_font(FB_FONT_16))
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
DECLARE SUB showData(buffer() AS UBYTE)
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 "test.bas" 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
ShowData src_buffer()
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
ShowData src_buffer()
SLEEP
END
SUB showData(buffer() AS UBYTE)
DIM i AS INTEGER
PRINT "Contents: ";
FOR i = 3 TO 36: PRINT CHR(buffer(i)); : NEXT
PRINT " [...]"
END SUB
OpenGL-Textur
Das Erstellen einer OpenGL-Textur mit einem Grafikpuffer 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
#INCLUDE ONCE "GL/gl.bi"
#INCLUDE ONCE "GL/glu.bi"
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
' Breite oder Höhe keine Potenz von 2
EXIT FUNCTION
END IF
REDIM dat(w * h * 4) AS UBYTE
p = CAST(UINTEGER PTR, @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)
' vertausche R und B, um das GL_RGBA-Texturformat zu nutzen
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
Zusätzliche Informationen und Funktionen | ||||
---|---|---|---|---|
|