How To: Load an OpenGL Texture using the FreeImage library (or FreeImagePlus, technically)

I’d been using DevIL (or OpenIL if you prefer) as my image loader library, only it hasn’t been updated in a long time and can be a pain to build properly, so I’ve needed to move onto something else. The DevIL fork, ResIL, isn’t quite ready for prime-time and my attempts to build it have resulted in wasted hours and failure (I’ve fixed multiple build errors on the way – but then I hit stuff I can’t see how to fix without trying to re-package the entire thing).

So, new image library, huh? I had a google and saw the FreeImage library (and it’s FreeImagePlus version for C++ – documentation for which can be found here) – and it just happens to be in the Arch community repos so a quick sudo pacman -S freeimage later and we’re ready to rock… Only it didn’t come with any examples, so I dug around on forums and found some code that just about worked, but I wasn’t a big fan of it – so I thought I’d rewrite it into a nicer, more robust method – and I think I’ve achieved just that.

A textured quad at an angle.

The loadTexture Method

 
// Method to load an image into a texture using the freeimageplus library. Returns the texture ID or dies trying.
GLuint loadTexture(string filenameString, GLenum minificationFilter = GL_LINEAR, GLenum magnificationFilter = GL_LINEAR)
{
    // Get the filename as a pointer to a const char array to play nice with FreeImage
    const char* filename = filenameString.c_str();
 
    // Determine the format of the image.
    // Note: The second paramter ('size') is currently unused, and we should use 0 for it.
    FREE_IMAGE_FORMAT format = FreeImage_GetFileType(filename , 0);
 
    // Image not found? Abort! Without this section we get a 0 by 0 image with 0 bits-per-pixel but we don't abort, which
    // you might find preferable to dumping the user back to the desktop.
    if (format == -1)
    {
        cout << "Could not find image: " << filenameString << " - Aborting." << endl;
        exit(-1);
    }
 
    // Found image, but couldn't determine the file format? Try again...
    if (format == FIF_UNKNOWN)
    {
        cout << "Couldn't determine file format - attempting to get from file extension..." << endl;
 
        // ...by getting the filetype from the filename extension (i.e. .PNG, .GIF etc.)
        // Note: This is slower and more error-prone that getting it from the file itself,
        // also, we can't use the 'U' (unicode) variant of this method as that's Windows only.
        format = FreeImage_GetFIFFromFilename(filename);
 
        // Check that the plugin has reading capabilities for this format (if it's FIF_UNKNOWN,
        // for example, then it won't have) - if we can't read the file, then we bail out =(
        if ( !FreeImage_FIFSupportsReading(format) )
        {
            cout << "Detected image format cannot be read!" << endl;
            exit(-1);
        }
    }
 
    // If we're here we have a known image format, so load the image into a bitap
    FIBITMAP* bitmap = FreeImage_Load(format, filename);
 
    // How many bits-per-pixel is the source image?
    int bitsPerPixel =  FreeImage_GetBPP(bitmap);
 
    // Convert our image up to 32 bits (8 bits per channel, Red/Green/Blue/Alpha) -
    // but only if the image is not already 32 bits (i.e. 8 bits per channel).
    // Note: ConvertTo32Bits returns a CLONE of the image data - so if we
    // allocate this back to itself without using our bitmap32 intermediate
    // we will LEAK the original bitmap data, and valgrind will show things like this:
    //
    // LEAK SUMMARY:
    //  definitely lost: 24 bytes in 2 blocks
    //  indirectly lost: 1,024,874 bytes in 14 blocks    <--- Ouch.
    //
    // Using our intermediate and cleaning up the initial bitmap data we get:
    //
    // LEAK SUMMARY:
    //  definitely lost: 16 bytes in 1 blocks
    //  indirectly lost: 176 bytes in 4 blocks
    //
    // All above leaks (192 bytes) are caused by XGetDefault (in /usr/lib/libX11.so.6.3.0) - we have no control over this.
    //
    FIBITMAP* bitmap32;
    if (bitsPerPixel == 32)
    {
        cout << "Source image has " << bitsPerPixel << " bits per pixel. Skipping conversion." << endl;
        bitmap32 = bitmap;
    }
    else
    {
        cout << "Source image has " << bitsPerPixel << " bits per pixel. Converting to 32-bit colour." << endl;
        bitmap32 = FreeImage_ConvertTo32Bits(bitmap);
    }
 
    // Some basic image info - strip it out if you don't care
    int imageWidth  = FreeImage_GetWidth(bitmap32);
    int imageHeight = FreeImage_GetHeight(bitmap32);
    cout << "Image: " << filenameString << " is size: " << imageWidth << "x" << imageHeight << "." << endl;
 
    // Get a pointer to the texture data as an array of unsigned bytes.
    // Note: At this point bitmap32 ALWAYS holds a 32-bit colour version of our image - so we get our data from that.
    // Also, we don't need to delete or delete[] this textureData because it's not on the heap (so attempting to do
    // so will cause a crash) - just let it go out of scope and the memory will be returned to the stack.
    GLubyte* textureData = FreeImage_GetBits(bitmap32);
 
    // Generate a texture ID and bind to it
    GLuint tempTextureID;
    glGenTextures(1, &tempTextureID);
    glBindTexture(GL_TEXTURE_2D, tempTextureID);
 
    // Construct the texture.
    // Note: The 'Data format' is the format of the image data as provided by the image library. FreeImage decodes images into
    // BGR/BGRA format, but we want to work with it in the more common RGBA format, so we specify the 'Internal format' as such.
    glTexImage2D(GL_TEXTURE_2D,    // Type of texture
                 0,                // Mipmap level (0 being the top level i.e. full size)
                 GL_RGBA,          // Internal format
                 imageWidth,       // Width of the texture
                 imageHeight,      // Height of the texture,
                 0,                // Border in pixels
                 GL_BGRA,          // Data format
                 GL_UNSIGNED_BYTE, // Type of texture data
                 textureData);     // The image data to use for this texture
 
    // Specify our minification and magnification filters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minificationFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnificationFilter);
 
    // If we're using MipMaps, then we'll generate them here.
    // Note: The glGenerateMipmap call requires OpenGL 3.0 as a minimum.
    if (minificationFilter == GL_LINEAR_MIPMAP_LINEAR   ||
        minificationFilter == GL_LINEAR_MIPMAP_NEAREST  ||
        minificationFilter == GL_NEAREST_MIPMAP_LINEAR  ||
        minificationFilter == GL_NEAREST_MIPMAP_NEAREST)
    {
        glGenerateMipmap(GL_TEXTURE_2D);
    }
 
    // Check for OpenGL texture creation errors
    GLenum glError = glGetError();
    if(glError)
    {
        cout << "There was an error loading the texture: "<< filenameString << endl;
 
        switch (glError)
        {
            case GL_INVALID_ENUM:
                cout << "Invalid enum." << endl;
                break;
 
            case GL_INVALID_VALUE:
                cout << "Invalid value." << endl;
                break;
 
            case GL_INVALID_OPERATION:
                cout << "Invalid operation." << endl;
 
            default:
                cout << "Unrecognised GLenum." << endl;
                break;
        }
 
        cout << "See https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml for further details." << endl;
    }
 
    // Unload the 32-bit colour bitmap
    FreeImage_Unload(bitmap32);
 
    // If we had to do a conversion to 32-bit colour, then unload the original
    // non-32-bit-colour version of the image data too. Otherwise, bitmap32 and
    // bitmap point at the same data, and that data's already been free'd, so
    // don't attempt to free it again! (or we'll crash).
    if (bitsPerPixel != 32)
    {
        FreeImage_Unload(bitmap);
    }
 
    // Finally, return the texture ID
    return tempTextureID;
}

Usage

To use the method, just set up your code something like this – in your main .cpp file at the top have:

#include <iostream>
#include <cstdlib>
 
// OpenGL/GLEW/GLFW/SDL/Whatever headers
 
// Include the FreeImagePlus library (don't forget to link your project to the .so/.a or to the lib/dll on Windows)
#include <FreeImagePlus.h>
 
using std::string;
using std::cout;
using std::endl;
 
// Create a handle for our texture
GLuint textureID;

In your initialisation/setup method add something like this:

//  ----- Initialise the FreeImage library -----
// Note: Flag is whether we should load ONLY local (built-in) libraries, so
// false means 'no, use external libraries also', and 'true' means - use built
// in libs only, so it's like using the library as a static version of itself.
FreeImage_Initialise(true);

In your main method, once you’ve create an OpenGL context (i.e. window) and called your initialise/setup method, then you can load a texture to use like this:

// Load an image and bind it to a texture
textureID = loadTexture("TestImage.png");
 
// Or load an image using trilinear filtering via mipmaps for minification and GL_LINEAR (the method's default) for magnification
textureID = loadTexture("TestImage.png", GL_LINEAR_MIPMAP_LINEAR);
 
// Or load an image using low quality mipmaps for minification and GL_NEAREST for magnification.
// The point is that you can set the minification and magnification parameters separately, not necessarily that you'd choose these interpolation options.
textureID = loadTexture("TestImage.png", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST);

Finally, on shutdown you may (optionally, as long as you’re not using the Linux/Mac .a version of the library) like to add the following:

// As we're using a .so version of the freeimageplus library on Linux we
// don't need to call DeInitialise (we would only really HAVE to do so on Linux
// or Mac [not Windows!] when using the .a version) - but it doesn't hurt to do it anyway.
FreeImage_DeInitialise();

Wrap-up

Not a great deal to say – it works, it should be relatively robust, it doesn’t leak memory (or at least none that we control), and the code is commented to the hilt and explains why it’s doing things instead of just what it’s doing (some people don’t like verbose code like this – but I teach a lot of programming so I just try to explain everything as clearly as possible as I go – and find I prefer it to ‘terse’ code that I have no idea WTF it’s up to or why it’s bothering).

Many thanks to the FreeImage devs for the library – it’s rather feature-packed and I quite like it (some good example code wouldn’t go amiss though!) =D

Hope you find the code useful! (Bugs? Comments? Suggestions? Feedback always welcome!).

How To: Insert data into a MySQL DB using Connector/C++

I created a guide to using MySQL Connector/C++ for a class a while back which showed a not-too-pretty way to put data into a DB, and I created a second part using prepared statements, locking, rollbacks and all that other good stuff – then I never posted it! Well, no more ;)

MySQL Connector/C++ Guide - Part 2

Details: MySQL & Connector/C++ Introductory Guide – Part 2 of 2
Format: Powerpoint 2003 (Best readability in Powerpoint 2003 onwards, LibreOffice and OpenOffice)
Slide Count: 19
Link: MySQL-Connector-Guide-Part-2.zip

If you find anything that needs correcting feel free to let me know! Cheers!

How To: Work with MySQL and Connector/C++

I threw together a set of slides on the topic for a class which was presented today… Thought maybe they’ll be of use to someone else.

MySQL & Connector Guide

Details: MySQL & Connector/C++ Introductory Guide
Format: Powerpoint 2007 and OpenOffice Impress
Slide Count: 26
Link: MySQL-Connector-Guide-1.zip

If you find anything that needs correcting feel free to let me know and I’ll make any changes as required.

Also, there’ll be a second set of slides posted soon on using prepared statements, rollbacks and enforcing transactional integrity – isn’t life fun? =P

Update: Second part now available here.

How To: Change from Shared Hosting to a VPS, Part 2 – Choosing a Hosting Provider, Signing Up & Connecting

I finally did it; I’ve made the jump from shared hosting to my own VPS slice – and I’m really rather liking things like this – control is good!

So, if you’re looking to make the jump too, this article should give you the first few stages of the how of things (whilst the previous Part 1 article dealt with the why) – so let’s make it happen, shall we?

Step 1 – Find Yourself a VPS Provider

There are thousands upon thousands of hosting companies who can offer you a VPS, ranging from fully managed (i.e. everything set up for you, but expensive) to unmanaged (where you set up everything yourself, and you pay much less per month). I’ve taken the latter route so I can gain some experience and pocket the difference.

The first thing you’ll want to do is take a look at some hosting sites which bring together offers from various hosting companies so you can compare and contrast them. I spent a while looking at a website called LowEndBox to find some good deals. Quite often, it seems, you can get offers which re-occcur, so if you sign up and enter a code you get 40% off or such, and this applies across the lifetime of your VPS hosting with the chosen company.

After a bit of looking I came across a new start-up company based in the UK called RackVM, who were offering a bunch of attractive packages for decent prices. Web hosting is one of those things where you get what you pay for, so be wary of going with companies based solely on them having the most absolute rock-bottom price you can find, as the service you get might not be too stellar. I’ve only been signed up with RackVM for about two weeks or so at this point, but during that time everything’s been great. Being a new start-up I’m running the risk that they fold in the near future, but I’ve got everything from my old site (files/database etc.) backed-up, and if I go dark I could just get another VPS provider and re-do the config to get it all back up and running in less than a day, so I’m not too concerned about it.

Update: DO NOT sign up with RackVM. I was signed up at 8GBP per month, but with a 40% recurring discount which made it 4.80GBP per month, and believe me, I was getting about 4.80GBP worth of hosting… The site’d be down multiple times per month,, support tickets would go unanswered, and their SLA is just a joke. I’m currently hosting this VPS with the much more expensive, yet infinitely better linode – and everything has been going perfectly.

When looking for a package there are a number of key factors to take into account (I can only really discuss this from a standpoint of what I, personally, wanted and signed up for – but you can happily extrapolate for your own needs):

  • Virtualisation Technology – The two main options for the technology that does the division of a single, physical machine into multiple VPS slices are OpenVZ and Xen. You can read a brief head to head comparison of them here or by putting something like “OpenVZ Vs Xen” into google. I chose OpenVZ for its lower memory footprint and increased speed, although either would have been fine, and likely Xen would have been a touch stabler and more secure as it runs a separate kernel per VPS slice rather than sharing a single kernel across all VPS’. Really, either option will be fine.

    Update: I’ve learned from experience and changed my mind – choose Xen. Really. Do not let your kernel get toppled by idiots on the same machine – segregate it with an iron fist!

  • Disk Space – How much file space do you have available? This is used for any packages you install on your VPS (web servers, FTP servers + storage area, MySQL databases – everything!), I really don’t need more than about 1GB or so for everything, but then I don’t really host a lot of files. The package I got came with 30GB – which is lots, and lots, of breathing room.
  • Bandwidth – How much data does the server need to upload to people? A site with a 300KB front page being hit 500 times a day will use 4.5GB per month at a minimum. When I signed up I got 300GB bandwidth per month, which is orders of magnitude more than I need, but things like FTP count into it, so if I upload something that’s 100MB, and it gets downloaded 50 times, that’s 5GB on top of whatever the site HTML will take, and when you add in running an email server it could easily get higher again. I still don’t see myself using more than 10GB a month in my wildest dreams, but if you can get a high number then you can always try to find inventive ways to use it (like maybe running sabnazbdplus or something).
  • RAM – this one’s important – really important. Remember that you’re going to be running a whole heap of things on the server and that it’ll eat through plenty of RAM. You might be able to get away with 256MB and keep 99% performance. 512MB is a lot nicer to work with and will give you a little bit of breathing room after correct configuration. I got 512MB with my package, and after tweaking apache to use a little less memory am running at roughly 370MB with apache, mysqld, php, postfix (Mail Transfer Agent), courier (POP3/IMAP Server), spamassasin, webmin and a couple of other bits and pieces running. It’ll spike up to 600MB under load, but then it drops back down below 512MB, which is my magic number.
  • Burst RAM – Every server running VPS’ will have more RAM than it needs so it can offer it on a temporary basis to any one of the VPS’ on the server. With my package I have up to 1GB Burst RAM, which means that when my site’s under load and the memory usage increases, I’ve got that additional 512MB to use. Be warned – if you go for a VPS with a low memory limit, and your site’s memory usage sits up in the Burst RAM all or most of the time, they’ll be on to you to upgrade your package to one that provides greater RAM, and costs more per month.
  • Operating System – OpenVZ and Xen can be run on any *nix, so you’ll have the option of picking your server platform from a bunch of choices like: Debian, Ubuntu, CentOS, FreeBSD or OpenBSD, Slackware, Gentoo etc. Some people say Debian is the metaphorical win as a server platform as it’s extensively tested and as such very, very stable. However, I chose Ubuntu 10.04 64-Bit as my server platform of choice, because 10.04 is a LTS (Long Term Support) version of Ubuntu, which means it’s gone through a pretty extensive testing period during its gestation, and as it’s pretty new it has all the latest versions of software available for that cutting-edge goodness. Also, I happen to run Ubuntu 10.04 as my OS of choice (normal, not server edition obviously), so anything I know works on my local machine is pretty much guaranteed to work on the server, and I can test things out on my local box before putting on the server.

Step 2 – Sign Up and Provide the Correct Server Details (Server name, NS1, NS2 etc.)

When you sign up, you’ll pick a package that gives you the combination of above options that best fit your needs, then you’ll need to enter some additional details for the VPS slice – you’re likely to see a screen asking you for details like this:

VPS Signup Server Settings

For this example, let’s say we have the site www.foobar.net on shared hosting, and we’re transitioning it to a VPS.

The first field, hostname, is asking you what you want to call your server. If it was just an email server, you might enter mail.foobar.net here, if you were going to have multiple servers serving files for the same domain (foobar.net) you might enter server1.foobar.net or server2.foobar.net. As I’m only ever going to have a single VPS serving everything, I thought I’d just call it server, and as such for our example I’d enter server.foobar.net and for my own particular instance, I entered server.r3dux.org.

The next two fields are asking you for the name of the primary and secondary nameservers that might run on the domain (i.e. what you would call them if you decide to run a DNS service such as bind) – they are NOT asking you for the name and address of the DNS servers which currently host the record for your shared hosting! It’s kind of like a “If you were a name server, what would you call yourself?”-type question =D For our example, we could happily put ns1.foobar.net for the primary nameserver and ns2.foobar.net for the secondary nameserver, and in my specific instance, I put ns1.r3dux.org and ns2.r3dux.org. I’m not even going to run a DNS service, as I simply don’t have to, I’m just specifying what I’d call the nameservers if I did!

The fourth and final field is asking for the password you want to set for your root account on your VPS. By this we mean, you have full access to your VPS using the top-level administrative account root, through which you will log in and administrate the the system. When your root account is created, on your VPS – what password do you want to use? I’d recommend setting this as something pretty strong (at least a dozen characters) which uses a combination of upper and lower case letters plus some numbers and punctuation, because if it gets compromised – your box will no longer be your box…

For our foobar.net example, this means that we’d end up with something like this:

VPS Signup Server Example Settings

With these details provided, proceed through the registration process, and you should be sent an order confirmation either:
– Detailing and confirming your order and asking for payment before it gets set up, or
– If you already paid at the registration stage, detailing the IP address of your spanky new VPS! (and optionally, a separate IP address and details of your username and password to connect to the server through SSH).

Step 3 – Connect to your Spanky New VPS!

Once you’ve paid and received an email giving you the IP address of your VPS, it’s time to connect to that bad boy!

VPS’ are servers, and as such don’t run graphical desktops – it’s all administered through the command line. So the best way to hook up to your VPS is through a SSH connection. With RackVM, remember that this is my first VPS so I have no other knowledge of what other VPS hosting providers do, they send you an email with a separate IP and username to use to connect to the VPS.

To connect to the VPS through linux, I can run the following command and enter my root password (the one we specified earlier in Step 2!):

ssh 1.2.3.4 -l <YOUR-SSH-USERNAME> -p 22

Where 1.2.3.4 is the IP address given for SSH access in the confirmation email. Once connected, you can install stuff using apt-get, or dpkg or whatever you like…

To connect to the VPS through a Windows system, your’re probably best off using PuTTY.

At this point, you can go to the HTTP address of your server (for example, http://5.6.7.8, where 5.6.7.8 is the IP address of your VPS server itself [and not the IP address of the SSH access – assuming that, like mine, they’re different]) – but there’s likely to be nothing at all there because Apache isn’t installed! If you wanted to install and run Apache quickly you could always SSH into your VPS and run:

apt-get install apache2
/etc/init.d/apache2 start

And then try http://5.6.7.8 (again, replacing 5.6.7.8 with your VPS IP address) – and TA-DAAAA! She lives!

Next Steps: After this we’ll want to configure Apache, add MySQL as a database server, add PHP, an Email server, add an FTP server and some Web Administration tools plus a couple of other bits and pieces – all the while keeping the memory usage low!

It’s easily do-able, it’s just a slightly lengthy process – which I’ll save for a Part 3 article, me thinks :)

Oh, and if you’ve seen anything in this article you think is wrong, technically inaccurate or plain bat-shit crazy – please feel free to correct me in the comments!

Cheers!