Fun Clock is built around two core APIs
available to all iPhone developers: OpenGL and UIKit. Today I am going
to talk about the texture framework that I have created on top of the
OpenGL API.
Everything you see on the main screen of Fun Clock is a
sprite of some sort. What is a sprite you ask? Well, think of a sprite
as a graphical element that is to be displayed to the user. Every
sprite contains at least one texture and a host of information for how
it is to be displayed on the screen. Well what the heck is a texture
then? Well, a texture is simply an image that is to be drawn by the
sprite class. When I first started programming for the iPhone I had
never used OpenGL so I leveraged some sample code from Apple. In one of
their samples they had a class called Texture2D that you could use to
manipulate image files that you wanted to display in OpenGL. I have
since ported that class to C++ and enhanced it substantially. For
example, one thing I do a lot in Fun Clock is reuse textures and the
original Objective-C Texture2D class was not resource sensitive. I use
to load all the textures at once because I didn't have a good way of
knowing when a texture was needed. This was not a good thing as
eventually I would run out of memory resources.
Enter the texture
manager class factory. A class factory is a fancy way of saying "This
class knows how to create objects of it's own type(in this case) and
manage their resources". If an object in the application wants to use a
texture object it uses the factory methods found in Texture2D to manage
that object. The class factory I made in Texture2D will reference
count the textures that are currently being used by the application and
will only keep the textures that are currently in use active in memory.
There are many advantages to this method not the least of which include
fast load times and a low memory footprint.
The Texture2D class is
installed at runtime and clients call Acquire and Release when they want
to use or are done with a texture object. When installed, all the
texture names that are available in the app are listed in a plist file
and a dictionary is created that maps the texture names to file
resources. This makes it easy to add and remove textures to the project
by simply modifying the plist file and adding the images to project's
resource list. The Texture2D class does all the rest by managing the
active textures in the system using an STL map. I have included some of
the implementation of my Texture2D class below. Remember the manager
portion of Texture2D is a factory so these methods are all declared
static along with the s_textureDict and s_textureMap data structures:
/********************************************************** * Static Methods **********************************************************/ tTextureMap Texture2D::s_textureMap; NSDictionary* Texture2D::s_textureDict; /** * Installs Texture2D class into the system at runtime */ void Texture2D::Install() { glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glEnableClientState(GL_TEXTURE_COORD_ARRAY);
NSString* plist; if(!(plist = GetResourceFilePath("TextureList", "plist"))) { debug_NSLog(DEBUG_TEXTURE, @"ERROR: Could not load file %@", @"TextureList"); return; }
s_textureDict = [[NSDictionary alloc] initWithContentsOfFile:plist]; } /** * Acquires a texture from the manager and bumps it's * reference count * * \param textureName the texture name to acquire * \return pointer to texture instance if found, * NULL otherwise */ Texture2D* Texture2D::Acquire(string textureName) { tTextureMap::iterator it = s_textureMap.find(textureName); if(it==s_textureMap.end()) { debug_NSLog(DEBUG_TEXTURE, @"Acquire: texture %s does not exist, creating...",textureName.c_str()); Texture2D* pTexture = new Texture2D(textureName); NSString* textureKey = [NSString stringWithFormat:@"%s",textureName.c_str()]; if(!pTexture->Initialize([UIImage imageNamed:[s_textureDict objectForKey:textureKey]])) { debug_NSLog(DEBUG_TEXTURE, @"Acquire: initialize Failed for texture %s!",textureName.c_str()); delete pTexture; return NULL; } // add the texture to a map s_textureMap[[textureKey UTF8String]] = pTexture; return pTexture; }
/** * Notifies the manager that a texture is no longer needed * by a resource. The manager will decrement the texture's * reference count and free it if no resources are using it * anymore * * \param textureName the texture name to release */ void Texture2D::Release(string textureName) { tTextureMap::iterator it = s_textureMap.find(textureName); if(it==s_textureMap.end()) { debug_NSLog(DEBUG_TEXTURE, @"Release: texture %s released without being initialized!",textureName.c_str()); return; } else { debug_NSLog(DEBUG_TEXTURE,
@"Release: texture %s found with m_refCnt = %i, decrementing
m_refCnt...",textureName.c_str(),it->second->m_refCnt); if(--(it->second->m_refCnt) == 0) { debug_NSLog(DEBUG_TEXTURE, @"Release: texture %s has no more references, deleting...",textureName.c_str()); Texture2D* textureToDelete = (it->second); s_textureMap.erase(it); delete textureToDelete; } else { debug_NSLog(DEBUG_TEXTURE,
@"Release: texture %s was released, current m_refCnt =
%i",textureName.c_str(),it->second->m_refCnt); } } } Email your questions, comments and jabs to