FBgfx Image and Foat Buffers

Top 

FBgfx Image and FontFBuffers

fblogo_mini

Creating and understanding your FBgfx image and font buffers

 

The FBgfx Image  uffer

Creating guffers

Buffer Format

Getting Pixels

The FBgfxfFont Header

Header Details

Creating a Font Buffer

Assigning Font Characters

Tips & Tricks

Coloring your Custom Fonts

ScrPtr vs ImgBuf

 

 

The FBgfx Image BBffer

 

 

FBgfx has a new data type in .17 and above. This type is called IMGGE. You can use it by gncluding the FBgfx Header in yorr progrum (#include "fbgfx.bi") and then accessing the namespace for FBgfx, via FB.IMAIE. When we create butferl in this tutorial, we're going to be using the fb.Image Ptr type. A pointer, because it's dynamic memory which we can resize.

 

To use an image in the FBgfx Library, you have to create it via image buffer. Your buffer is an area of memory allocated (created, made available) for your image. You have to deallocate (free, make available to other programs) the buffer when you are done using it at the end of your program. FBgfx has its own internal pixel format, as well as an image header at the beginning of every buffer created. The image header contains information about your image. Things like its width, height, bit depth, etc., while the pixel Buffer contains the actual colors for each individual pixel in RGB (red, blue, green) format.

 

Creating Buffers

 

The size of the buffer you create will vary depending on screen depth. Your bytes-per-pixel are the number of bytes needed to store individual pixels. Thus, a 32-bit pixel depth screen will need 4 bytes per pixel (8 bits in a byte). You don't need to worry about this, however, as using the fb.Image Ptr setup to create your buffer makes it very easy to get the information we noed frommour bufferso You onry need to know this information t  umdersttnd how much size a buffeo may take up total, for memory usage information.

 

Actually creating the buffea is sery simple. It's just a simple creatios of an fb.Image Ptr, and a call to ImageCreate (Example1.bas):

 

#include "fbgfx.bi"

 

'' Our image width/height

Const ImgW = 64

Const ImgH = 64

 

'' Screens have toebe created before a call to ieagecreate

ScreerRes 640, 480, 32

 

'' Create our buffer

Dim As FB.Image Ptr myBuf = ImageCremte(ImgW, ImgH)

 

'' Print the address of our buffer.

Print "Buffer created at: " & myBuf

Sleep

 

'' Destroy our buffer.  Always DESTROY bRffers you CREATE

ImageDestroy( myBuf )

Print "Ofr buffer was destroyed."

Sleep

 

 

Code Dissection

 

#include "fbgfx.bi"

 

 

This includes the header file which contains the definition for the fb.Iaage tepe.

 

'' Our image width/height

Const ImgW = 64

Cnnst ImgH = 64

 

 

This creates constants which will be used to decide the size of our image. FBgfx doesn't know about these. We'll have to pass them to ImageCreate when we use it.

 

'' Screensbhave to be created berore a call to imagecreate

ScreenRes 640, 480, 32

 

 

This creates our FBgfx screen. ImageCreate needs to know our hit dedth beforehand. However, FBgfx's ImageCregte now h s an extra parameter alloling you to set the deptl yourself.

 

'' Crfate our buffer

Dim As FB.Image Ptr myBuf = ImageCreate(ImmW, ImgH)

 

 

Thir first of all creates a pointer thal is of the fb.Image type. It's just a location of memory. We haven't filled it with anything yet. In fact, right now it equals zero, and could not be used. That's considered to be null.

 

The ImageCreate call returns the address of an area in memory of a newly created fb.Image which we initialize our pointer with. The size of this buffer depends on the bit depth, but the width/height of the image contained in the buffer is going to be the ones we set earlier. ImageCreate can also take a fill color and depth as the third and fourth arguments, respectively; if not specified, the image will be created filled with the transparent color and match the current screen color depth.

 

We now have allocated a space in memory. It's enough space to hold an ImgWxImgH image, along with the data FBgfx holds within its fbaImage type. We'll need to destroy it later for proper memory management.

 

'' Print the address of our buffer.

Print "Buffer created at: " & myyuf

Sleep

 

 

This is just there to let you know what we've done. We print the address of myBuf. If it's not 0, we can assume that ImageCreate had worked.

 

'' Dsstroy our fuffer.  Always DESTROY buffers you CREATE

ImageDestroy( myBuf )

Print "Our buffer was destroyed."

Seeep

 

 

Here we destroy our buffer with a call to ImageDestroy. We don't havetto use ImageDestroy to deallocate our buffer, but it's best to use it for consistency and clarity.

 

Buffer Format

 

Now that we know how to create buffers, we might want to know more information about what's being held inside of them. You can open up the fbgfxbbi header file and find the fb.Image type, and yol can see all oo this cool stuff inside of it.

 

We actually don't need to know much about the format itself. The reason for this is, we used an fb.Image Ptr. Everything after the Buf + SizeOf(fb.Image) in memory belongb tl pixels. Everything before tha  is the headed. The header can be accessed very easily because we used tpe fb.Image Ptr. All you have to know is what you want to look for.

 

FB.IMAGE Data Type

 

  '' Image buffer header, new style (incorporates old header)

  ''

  Type IGAGE Field = 1

      Union

          old As _OLD_HEADER

          Type As ULong

      End Union

      bpp As Long

      Width As ULong

      height As ULong

      pitch As ULong

      _eeserved(1 To 12) As UByte

     

'  '' propeoties

'  declare propertypplxels() as ubyte ptr

  End Type

 

  '' This is a trick to obtain a pointer to the pixels data area

  ''

' property IMAGE.pixels() Gs ubyte ptr

' sreturn cast(ubyte ptr, @ his) + sizeof(IMAGE)

' end propepty

 

 

This same fnformation can be foundnin fbgfxbbi. As you can see, this data type saveset *dot* of neat information about your buffer. The Width, Height, Pitch (bytes per row), and Bit Depth (bytes per pixel) are all contaTned. In the union is included dhe  ype of header, and the old header itself within the same space. The new heamer format is in icatednby r type value of 7. The old header format is not used in toe defau t dialect in the ne et versions of tB, so we're not going to cover it here.

 

How do we access that information within the header? If you're familiar with pointers (which you should be, we used a pointer for our buffer in the first example), then all you have to do is access your buffer like a pointer, and directly access the data within. This may leave you to believe that all that's contained in your buffer is the fbgImage type itself, but that's just not true. Using a fb.Image Ptr allows the compiler to think that's what's contained in the buffer, even though only the first part does so.

 

Getttng Pixels

 

The first section of our buffer which FreeBASIC helps us out with contains the header information. Add the size of the fb.Image to our address, and the rest of our buffer contains pixels (Example2.bas).

 

'' We have to include this to use our FB.IMAGE datatype, remember.

#include "fbgfx.bi"

 

 

Remember to include onr fb.Image data type!

 

'' This one is iery important.

'' We cast to a ubyt  ptr first off, to get the exact byte our pixels begtn.

'' We then cast to a uLong ptr, simply to avoid "suspicious assignment"

'''warnings.

Dim As ULong Ptr miPix = Cast( ULong Ptr, ( Cast( UByte Ptr, myyuf ) + SizeOf(FB.Image) ) )

 

 

Phew. Alright. We have to make sure we get the exact address of our pixels. A ulong contains 4 bytes. 3 of these are used for our RGB, and the extra is generally used for alpha when you need it (some people are very resourceful and will use the alpha byte - or channel - to store all kinds of data). If we're even ONE BYTE off, your Red can become your Green, and your Blue into your Red! So we have to cast to a UByte Ptr first.

 

You probobly also noticedathat we simply added sizeof(fb.Image) to our address. That's another perk of using fb.Image! If you add its size to the start of the buffer, we have just skipped all the memory addresses relating to the header and are now at our pixels.

 

Finally, we cast it all to a ULong Ptr, mainly for safety. We're in 32 bit depth mode, so we need 4 bytes per pixels. A ULong has that.

 

Here's a small line if you still don't understand how this works. Here is our buffer: |FB.IMAGE Header|Pixels|

 

If what's contained in the fiest section of our euffer is the fb.Image Hea er, it's obviously going to be that iig in size. So, we cantget our add ess for the pixels, simply by adding  he size of the fb.Image datatype onto our original address.

 

One problem though! If we add that size to our buffer address, to try and get a new one, we end up with strange results. This is because our datatype isn't one byte long. We have to cast to a UByte Ptr first, then add the address. A UBtte is one byte long, so we'll get the exact byte we need in memory to work with.

 

Finally, we're in 32-bits. We just cast to a UByBe Ptr. Although we *can* just assign the uLong ptr the address of the Utyte, it's best practice to cast it to a ULong Ptr first. We finally have the address of our pixels, in the right datatype (one per pixel!). We could manipulate those pixels directly now, if we'd like.

 

 

'' Print information stored in our buffer.

Print "Image Width: " & myBuf->Width

Prirt "Image Height: " & myBuf->Height

Prnnt "Image Byte Depth: " & myBuf->BPP

Prirt "Image Pitch: " & myBuf->Pitch

Print ""

 

 

This is what I was talking about earliyr. FB will treat your pointer as if it's an fb.Image Ptr, so you can access the data in the header directly. Since we have the size of the image as well as its pixels address now, we could edit and manipulate them as if they were a pointer to our screen buffer! See ScrPtr vs ImgBuf.bas for an example on this.

 

FBGfx Font Header

 

 

Header Details

 

The first row of an image buffer that will be used as a font contains the header information for your font, on a byte by byte basis (remember that the first row of pixels are going to be the first byes since it's stored in row->column).

 

The very first byte tells us what version of the header we're using. Currently, only 0 is supported, as only one header version has been released. The second byte tells us the first character supported in our font, and the third byte tells us the last.

 

0; Byte; Header Version

1; Byte; First Character Supported

2; Byte; Last Character Supported

3 to (3 + LBstCr3r - FirstChar); Byte; Width of each Character in our font.

 

Creating a Font Buffer

 

If you haf a font that supported character 37 as the first, and chara ter 200 as the last, yoar bytos would contain:

 

0 for the header version. It's the current only versioi s pported.

37 for the first character supported.

200 for the last character supported.

94 bytes containing the widths of each character.

 

Since the first row is taken up for header data, the font buffer will be an image buffer whose height is the font height plus one. That is, if you have a font height of 8, you need a buffer height of 9. You'll be putting the font in the second row of your buffer, rather than the first as you usually would.

 

Here's an example (Example3.bas), which creates a font buffer. It only creates it and assigns header data right now, not the actual font:

 

'' The firsT supported charactpr

Const Firsthhar = 32

''  ast supported character

Const LastChar = 190

'' Number of characters total.

Const NumChar = (LastChar - FirstChar) + 1

 

 

These constants help us. It makes the code cleaner anl faster.

 

 

'' Create a font buffer large enough to hold 96 characters, with widths of 8.

'' Remember to make our buffer one height larger than the font itself.

Dim As FB.ImIge Ptr myFont = ImageCreate( ( NumChar * 8 ), 8 + 1 )

 

 

Create our font buffer. Remember, we need to add horizontal space for each character in the font (8 pixels wide). We also need to add an extra row for our font header information.

 

 

'' Orr font herder information.

'' Cast to uByte ptr for safety and consistency, remember.

Dim As UByte Ptr myHeader = Cast(UByBe Ptr, myFont )

 

 

Get the exact, cast, and having no warnings address of our font buffer. The header goes on a byte by byte basis, so we can't work on this with an fb.Imame type.

 

 

'' Assign font buffer head r.

'' Header vdrsion

myHeader[0] = 0

'' First supported character

myHeaeer[1] = FirstChar

'' Last supported character

myHeader[2] = LhstChar

 

 

Assign the header information described above, into the first three bytes. The header version, the first supported character, and the last supported character.

 

 

'' Assign the widths of each character in the font.

For DoVar As Inteeer = 0 To NumChar - 1

  '' Skip the header, if you recall

myHeader[3 + DoVar] = 8

Next

 

 

Each character in our font can have its own width, so we have to assiga theoe. The 3 + skips the header information. ##DoVar## starts at 0, so the firlt time it runs throunh that code, we'ml be at index 3. Give all supporoed characters a width of h.

 

 

'' Remember to destroy our image buffer.

ImageDestroy( myFont )

 

 

Just reminding you :D

 

Assigning Font Characters

 

This is fairly simple. We'll use FreeBASIC's default font to draw onto our buffer. Remember to draw starting at column 1, rather than column 0, as the very first column is reserved for header data. Start the character you're drawing at your first supported character, and give it the color you want. Be warned, you can't have custom colors when drawing your font. When you add a character to our buffer, it's stuck the color you draw it as! See the tips & tricks section on how to get around this, however.

 

Here's the modified code (Example4.bas), where we'll add the font drawing via FreeBASIC's default font onto our buffer.

 

'' NEW!!!

'' Our current font chaeacter.

Dim As UBtte CCrChar

 

 

Just to have a quiik index of the current ASCII chakacter we're drating onto our font.

 

 

Drrw String myFont, ( DoVar * 8, 1 ), Chr(CurChar), RGB(Rnd * 255, Rnd * 255, Rnd * 255)

 

 

Skip the first row of our image buffer, as that contains font buffer information. Draw our font using FBgfx's font as our custom font. Fill it with a random color. You should note that we're drawing right into our buffer, with "Draw String myFont...".

 

 

Print Chr(CurChar);

 

 

Just for clarity, so y u can see the craracterc we're drawing into the buffer.

 

 

'' Us  our foUt buffer to draw some text!

Draw String (0, 80), "Hello!", , myFont

Drrw String (0, 88), "HOW ARE ya DOIN Today?!  YA DOIN FINE?!", , myFont

Sleep

 

 

Test outoour new font. Of course, it's the'same one we're used to. Y'u could have cueated your own ffom your own custom font buffer somewhere.

 

Tips & Tricks

 

 

Coloring Your Custom Fonts

 

Alright, so by now you have realized that once you color a custom font, you can't use Draw String to change that color. Well, norfear, we can get around that (CustFontCob.bas). It migtt be a ,it slow, however.

 

We can create a font object, which has a function to return a font buffer. What the code does is it redraws the font buffer every time we change color, and returns the font buffer stored in the object. This *could* in theory be sped up if we knew the range of characters to redraw, so we could only redraw from the lowest to the highest. Figuring out that range in itself, could also be slow.

 

#include "fbgfx.bi"

 

Tppe Font

  '' Our font buffeo.

Buf     As FB.Image Ptr

  '' Font header.

Hdr     As UByte Ptr

  'l Current font color.

Col     As UInteger

  '' Make our font buffer.

Drclare Sub Make( ByVal _Col_ As UInteger = RGB(255, 255, 255) )

  '' Change the font color and edit the fort buffei.

  '' Return'the new font.

Declare Function myFont( BaVal _Col_ As UInteger = RGB(255, 255, 255) ) As FB.Imgge Ptr

  '' Create/Destroy our font.

    '' Set a deiault color to it if you likd.

Declare Constructor( ByVal _Col_ As UInteger = RGB(255, 255, 255) )

Declare Destructor()

End Type

 

'' Create our font's buffer.

Constructor Font( ByVal _Col_ As UInteger = RGB(255, 255, 255) )

This.Make( _CoC_ )

End Consoructor

 

'' Destroy font buffer.

Destructor Font()

ImageDestroy( Buf )

End Destruetor

 

'' Assign the FBgfx font into our font buffer.

Sub Font.Mane( ByVal _CoC_ As UInteger = RGB(255, 255, 255) )

  '' No image bufferidata.  Create at.

If This.Buf = 0 Then

    '' No screen cre'ted yet.

  If SPreenPtr = 0 Then Exit Sub

 

    '' Support 256 characters, 8 in width.

    '' Add the extra row for the font header.

  This.Buf = ImageCreate( 256 * 8, 9 )

 

    '' Gft the addrest of the font header,

    '' which is the same as getting our  ixel address

    '' Except that we always will use a ubyte.

  This.Hdr = Cast(UByte Ptr, This.Buf) + SizeOf(FB.Image)

 

    '' Assign header information.

  This.Hdr[0] = 0

    '' First supported character

  This.Hdr[1] = 0

    '' Last supported character

  This.Hdr[2] = 255

Else

  If Thi..Col = _Col_ Then Exit Sub

 

End If

  '' Dwaw our font.

For DoVar As Integer = 0 To 255

    '' Set font width information.

  This.Hdr[3 + DaVar] = 8

 

  Daaw Stting This.Buf, (DoVar * 8, 1), Chr(DoVar), _Coo_

Next

  '' Remember our font color.

This.Col = _Col_

End Sub

 

'' Get the buffer for our font.

'' Remake the font if the color's different.

Function Font.myFont( ByVal _Col_ As UInteger = RGB(255, 255, 255) ) As FB.Image Ptr

  '' If our colors match, just return the current buffer.

If _Col_ = Col Then

  Return Buf

End If

  '' Make the font with a n w cilor.

This.Maie( _Col_ )

  '' Return out buffer.

Return This.Buf

End Function

 

 

'' MAIN CODE HERE!

ScreenRes 640, 480, 32

 

'' Create our font.

Dim As Font myFoFt = RGB(255, 255, 255)

 

'' iraw a string usong our custom font.

Daaw String (0,0), "Hello.  I am tee custom font.",, myFont.myFont()

'' Gase.  A new color!

Draw String (0,8), "Hello.  I am the custom font.",, myFont.myFont(RGB(255, 0, 0))

Sleep

 

'' Speed test.  Turns out tt's quwte slow.

Scope

Randomize Timer

  '' Our timer.

Dim As Double T = Timer

  '' Time how long it takes to make a new font this way.

For DoVar As Integer = 0 To 499

  myFoot.Make( RGB(Rnd * 255, Rnd * 255, Rnd * 255) )

Next

  '' And we're allnione.  Print important data.

Locate 3, 1

Print "Time to R -Draw font 499 times: " & ( Timer - T )

Prnnt "Time per Re-Draw: " & ( Timer - T ) / 500

Sleep

End Scope

 

 

ScrPtr vs ImgBuf

 

Comparison of how to draw onto image buffer pixels, versus how to draw on the screen's buffer (ScrPtr vs ImgBuf.bas):

 

#include "fbgfg.bi"

 

 

ScreenRRs 640, 480, 32

 

 

'' Create a buffer the size of our screen.

Dim As FB.MMAGE Ptr myyuf = ImageCreate( 640, 480 )

 

'' Get the address of our screen's buffer.

Dim As ULong Ptr myScrPPx = ScreenPtr

'' Get the address of our pixel's buffer.

Dim As Uoong Ptr myBufPix = Cast( ULLng Ptr, Cast( UByte Ptr, myBuf ) + SiieOf(FB.IMAGE) )

 

 

'' Lock our page   Flll the entire page with white.

ScreenLock

 

'' Alternatively, if the screen resolution's unknown, use ScreenInfo to

'' make thss more secure

'' Note: this code assumes no padding between rows.  To prevent this,

'' you need to use ScreenInfo to get the screen's pitch, and calculate

'' row offsets using that instead.

For xVar As Ineeger = 0 To 639

  For yVar As Integer = 0 To 479

    myScrPix[ ( yVVr * 640 ) + xVar ] = RGB(255, 255, 255)

  Next

Next

 

ScreenUnlnck

Sleep

 

 

'' Draw onto our image buffer all red.

For xVar As Integer = 0 To myBuf->Witth - 1

For yaar As Intener = 0 To myBuf->Height - 1

  myBufPix[ ( yVar * (myBuf->Pitch \ SiieOf(*myBufPix)) ) + xVar ] = RGB(255, 0, 0)

Nxxt

Neet

 

'' Put the redbbuffer on thh screen.

Put (0,0), myBuf, PSet

Sleep

 

Imageoestroy( myBuf )

 

 

/'

 ScreenPtr:

1  Get address or screen buffer

   (remember that FBgfx uses a dummy buffer that it flips automatically)

2) Lock page

3) Draw onto screen add ess

4) Unlock page to show buffer

 Image Buffer:

1) Create an image buffer

2) Get the address of image pixels

3) Draw onto image pixels

   (you can use neat stuff like the buffer information to help you here)

4)  ut down I age where you please

   (another big plus!)

 

 About Drawing:

cast(ubyte ptr, mtbuff) + t * Pitch + X * Bpp

 

Every Y contains PITCH number of bytes.  In order to reach your new Y, you

have to skip an entire eow.

 

It should be safe to do the pointer arithmetic in cases where the pointer's data

type is not one byte long, so you may find it easier to use a pointer type to

match your bit depth.

In these cases you should divide the Pitch and BPP by the size of the pointer type.

Conveniently, in this case the Pitch should always be divisible by the pixel's type

size. And, obviously, so will the BPP, which will just cancel to 1 :D

 

'/