Who's Online
We have 3 guests online
Feb 22, 2012 at 10:37 AM

Texture Management
Written by Brian Glover   
Nov 18, 2010 at 10:22 AM
    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;
    }

    Texture2D* pTexture = s_textureMap[textureName];
    debug_NSLog(DEBUG_TEXTURE, @"Acquire: texture %s exists, incrementing m_refCnt...", textureName.c_str());
    pTexture->m_refCnt++;
    debug_NSLog(DEBUG_TEXTURE, @"Acquire: texture %s acquired with refCnt = %i",textureName.c_str(),pTexture->m_refCnt);
   
    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
Last Updated ( Nov 18, 2010 at 01:23 PM )
Fun Clock Landscape Configuration Mode
Written by Brian Glover   
Nov 01, 2010 at 03:54 PM

Let's talk new Fun Clock features!  I have added a landscape mode to the configuration screens for the app.  I think I finally got all the little bugs worked out of the mode that will be coming in Fun Clock 1.01.  I think it makes the UI much more convenient to use especially those of you who are using Fun Clock mostly in landscape mode.  Most of the configuration pages use UIPickerViews which do not lend themselves to displaying nicely in landscape mode on the iPhone.  A UIPickerView just takes up too much screen real estate to be used at full width in that layout.  So, instead of switching to a table layout I kept the UIPickerViews but made them into little squares that are positioned in such a way that they can easily be manipulated using your thumb.  

Landscape Config Page

One thing I was not able to get into landscape mode is the music picker control for the alarm.  It is a MPMediaPickerController which does not support landscape mode.  I tried to make it work but found the Cancel button was not "clickable" in that mode.  After some research(um...google) I finally found that Apple doesn't want you using that control in landscape mode.

It's actually in Apple's docs:

"Notes: The MPMediaPickerController class supports portrait mode only. This class does support subclassing. The view hierarchy for this class is private; do not modify the view hierarchy."

So, I think Fun Clock will be stuck with this until either Apple changes that control or I get REALLY motivated and I make my own control.

Last Updated ( Nov 18, 2010 at 01:23 PM )


Search
Enter Keywords: