FBgfx Image Information

    The FBgfx Image Buffer
        Creating Buffers
        Buffer Format
        Getting Pixels
    The FBgfx Font Header
        Header Details
        Creating a Font Buffer
        Assigning Font Characters
    Tips & Tricks
        Coloring your Custom Fonts
        ScrPtr vs ImgBuf
    
The FBgfx Image Buffer:

    FBgfx has a new data type in .17 and above.  This type is called IMAGE.  You can use it by including the FBgfx Header in your program (#include "fbgfx.bi") and then accessing the namespace for FBgfx, via FB.IMAGE.  When we create buffers 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 need from our buffers.  You only need to know this information to understand how much size a buffer may take up total, for memory usage information.
        
        Actually creating the buffer is very simple.  It's just a simple creation of an FB.Image ptr, and a call to Image Create (Example1.bas):
        
        #include "fbgfx.bi"

          '' Our image width/height
        Const ImgW = 64
        Const ImgH = 64

          '' Screens have to be created before a call to imagecreate
        Screenres 640, 480, 32

          '' Create our buffer
        Dim as FB.Image ptr myBuf = ImageCreate(ImgW, ImgH)

          '' Print the address of our buffer.
        Print "Buffer created at: " & myBuf
        sleep

          '' Destroy our buffer.  Always DESTROY buffers you CREATE
        ImageDestroy( myBuf )
        Print "Our buffer was destroyed."
        sleep
        
    Code Dissection:
        
        #include "fbgfx.bi"
        
            This includes the FB.IMAGE type which is very useful.
            
        
          '' Our image width/height
        Const ImgW = 64
        Const 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 the imagecreate function when we use it.
            
 
          '' Screens have to be created before a call to imagecreate
        Screenres 640, 480, 32
            
            This creates our FBgfx screen.  Imagecreate needs to know our bit depth beforehand.  However, FBgfx's ImageCreate now has an extra parameter allowing you to set the depth yourself.  See http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgImagecreate for the full details.
            
            
          '' Create our buffer
        Dim as FB.Image ptr myBuf = ImageCreate(ImgW, ImgH)

            This first of all creates a pointer that 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 (which we copy onto our pointer as can be see myBuf = ImageCreate).  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.  The full use of ImageCreate is ImageCreate( Width, Height, Color, Bit Depth ), but we only need to set the Width and Height since we already know the depth.  We'll use the default transparency color.
            
            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 FB.Image type.  We'll need to destroy it later for proper memory management.
            
            
          '' Print the address of our buffer.
        Print "Buffer created at: " & myBuf
        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.
            
            
          '' Destroy our buffer.  Always DESTROY buffers you CREATE
        ImageDestroy( myBuf )
        Print "Our buffer was destroyed."
        sleep

            Here we destroy our buffer with a call to ImageDestroy.  We don't have to 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 FBgfx.bi and find the IMAGE type, and you can see all of 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 belongs to pixels.  Everything before that is the header.  The header can be accessed very easily because we used the 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 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

        This same information can be found in fbgfx.bi.  As you can see, this data type saves a *lot* of neat information about your buffer.  The Width, Height, Pitch, and Bit Depth are all contained.  In the union is included the type of header, and the old header itself within the same space.  To be honest, I don't want to support the old header.  Not enough features, so we're not going to cover it.
        
        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 IMAGE type itself, but that's just not true.  DIMing as an FB.IMAGE ptr allows FB to think that's what's contained in the buffer, even though only the first part does so.
        
    Getting Pixels:
        
        The first section of our buffer which FB helps us out with contains the header information.  Add the sizeof the FB.IMAGE type 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 our FB.IMAGE data type!
            
            
          '' This one is very important.
          '' We cast to a ubyte ptr first off, to get the exact byte our pixels begin.
          '' We then cast to a uInteger ptr, simply to avoid "suspicious assignment"
          '' warnings.
        Dim as uInteger ptr myPix = cast( uInteger ptr, ( cast( ubyte ptr, myBuf ) + sizeof(FB.Image) ) )
        
            Phew.  Alright.  We have to make sure we get the exact address of our pixels.  An integer 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 probably also noticed that we simply added the 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 uInteger ptr, mainly for safety.  We're in 32 bit depth mode, so we need 4 bytes per pixels.  A uInteger 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 first section of our buffer is the FB.IMAGE Header, it's obviously going to be that big in size.  So, we can get our address for the pixels, simply by adding the 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 ubyte 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 casted to a ubyte ptr.  Although we *can* just assign the uInteger ptr the address of the uByte, it's best practice to cast it to a uInteger 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
        Print "Image Height: " & myBuf->Height
        Print "Image Bit Depth: " & myBuf->BPP
        Print "Image Pitch: " & myBuf->Pitch
        Print ""
        
            This is what I was talking about earlier.  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 + LastChar - FirstChar); Byte; Width of each Character in our font.
        
    Creating a Font Buffer:
    
        If you had a font that supported character 37 as the first, and character 200 as the last, your bytes would contain:
        
        0 for the header version.  It's the current only version supported.
        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 character
        Const FirstChar = 32
          '' Last supported character
        Const LastChar = 190
          '' Number of characters total.
        Const NumChar = (LastChar - FirstChar) + 1
        
            These constants help us.  It makes the code cleaner and 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.Image 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.
        
        
          '' Our font header information.
          '' Cast to uByte ptr for safety and consistency, remember.
        Dim as uByte ptr myHeader = cast(uByte ptr, myFont )
        
            Get the exact, casted, 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.IMAGE type.
            

          '' Assign font buffer header.
          '' Header version
        myHeader[0] = 0
          '' First supported character
        myHeader[1] = FirstChar
          '' Last supported character
        myHeader[2] = LastChar
        
            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 Integer = 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 assign these.  The 3 + skips the header information.  DoVar starts at 0, so the first time it runs through that code, we'll be at index 3.  Give all supported characters a width of 8.


          '' Remember to destroy our image buffer.
        ImageDestroy( myFont )
        
            Just reminding you :D
            
    Assigning Font Characters:
    
        This is fairly simple.  We'll use FB'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 FB's default font onto our buffer.
        
          '' NEW!!!
          '' Our current font character.
        Dim as uByte CurChar
        
            Just to have a quick index of the current ASCII character we're drawing onto our font.
          
          
        Draw 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 you can see the characters we're drawing into the buffer.
        

          '' Use our font buffer to draw some text!
        Draw String (0, 80), "Hello!", , myFont
        Draw String (0, 88), "HOW ARE ya DOIN Today?!  YA DOIN FINE?!", , myFont
        sleep
            
            Test out our new font.  Of course, it's the same one we're used to.  You could have created your own from 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, no fear, we can get around that (CustFontCol.bas).  It might be a bit 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"

        Type Font
            '' Our font buffer.
          Buf     as FB.Image Ptr
            '' Font header.
          Hdr     as uByte ptr
          
            '' Current font color.
          Col     as uInteger
          
            '' Make our font buffer.
          Declare Sub Make( byVal _Col_ as uInteger = rgb(255, 255, 255) )
            '' Change the font color and edit the font buffer.
            '' Return the new font.
          Declare Function myFont( byVal _Col_ as uInteger = rgb(255, 255, 255) ) as FB.Image ptr
          
            '' Create/Destroy our font.
              '' Set a default color to it if you like.
          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( _Col_ )
        End Constructor

          '' Destroy font buffer.
        Destructor Font()
          ImageDestroy( Buf )
        End Destructor

          '' Assign the FBgfx font into our font buffer.
        Sub Font.Make( byVal _Col_ as uInteger = rgb(255, 255, 255) )
            '' No image buffer data.  Create it.
          If This.Buf = 0 then
          
              '' No screen created yet.
            If Screenptr = 0 then Exit Sub
            
              '' Support 256 characters, 8 in width.
              '' Add the extra row for the font header.
            This.Buf = ImageCreate( 256 * 8, 9 )
            
              '' Get the address of the font header,
              '' which is the same as getting our pixel 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 This.Col = _Col_ then Exit Sub
            
          End If
          
            '' Draw our font.
          For DoVar as Integer = 0 to 255
              '' Set font width information.
            This.Hdr[3 + DoVar] = 8
            
            Draw String This.Buf, (DoVar * 8, 1), chr(DoVar), _Col_
          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 new color.
          This.Make( _Col_ )
            '' Return out buffer.
          Return This.Buf
        End Function


          '' MAIN CODE HERE!
        Screenres 640, 480, 32

          '' Create our font.
        Dim as Font myFont = rgb(255, 255, 255)

          '' Draw a string using our custom font.
        Draw String (0,0), "Hello.  I am the custom font.",, myFont.myFont()
          '' Gasp.  A new color!
        Draw String (0,8), "Hello.  I am the custom font.",, myFont.myFont(rgb(255, 0, 0))
        sleep

          '' Speed test.  Turns out it's quite 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
            myFont.Make( rgb(rnd * 255, rnd * 255, rnd * 255) )
          Next
          
            '' And we're all done.  Print important data.
          Locate 3, 1
          Print "Time to Re-Draw font 499 times: " & ( Timer - T )
          Print "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 "fbgfx.bi"


        Screenres 640, 480, 32


          '' Create a buffer the size of our screen.
        Dim as FB.IMAGE ptr myBuf = ImageCreate( 640, 480 )

          '' Get the address of our screen's buffer.
        Dim as uInteger ptr myScrPix = ScreenPtr
          '' Get the address of our pixel's buffer.
        Dim as uInteger ptr myBufPix = Cast( uInteger ptr, Cast( uByte ptr, myBuf ) + Sizeof(FB.IMAGE) )


          '' Lock our page.  Fill the entire page with white.
        Screenlock

          '' Alternatively, if the screen resolution's unknown, use ScreenInfo to
          '' Make this more secure
          For xVar as Integer = 0 to 639
            For yVar as Integer = 0 to 479
              myScrPix[ ( yVar * 640 ) + xVar ] = rgb(255, 255, 255)
            Next
          Next

        Screenunlock
        sleep


          '' Draw onto our image buffer all red.
        For xVar as Integer = 0 to myBuf->Width - 1
          For yVar as Integer = 0 to myBuf->Height - 1
            myBufPix[ ( yVar * myBuf->Width ) + xVar ] = rgb(255, 0, 0)
          Next
        Next

          '' Put the red buffer on the screen.
        Put (0,0), myBuf, pset
        sleep


        /'
          ScreenPtr:
            1) Get address of screen buffer
               (remember that FBgfx uses a dummy buffer that it flips automatically)
            2) Lock page
            3) Draw onto screen address
            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) Put down Image where you please
               (another big plus!)
               
          About Drawing:
            (Y * Width) + X
            
            Every Y contains WIDTH number of pixels.  Remember that the entire screen's
            buffer is all held sequentially.  In order to reach your new Y, you have to
            skip an entire row of pixels :D
            
            
            1 Row = Width Number of Pixels (640)
            1 Column = Height Number of Pixels (480) - For Y,X drawing rather than X,Y
            
            Y = Row * Width then.
            Just add X to get the horizontal position on this row.
            
            X = Column * Height then.    
            Just add Y to get the vertical position on the column.
        '/