Subversion Repositories spk

Rev

Blame | Last modification | View Log | RSS feed

// TextureFile.cpp: implementation of the CTextureFile class.
//
//////////////////////////////////////////////////////////////////////

#include "TextureFile.h"

#include <stdlib.h>
#include <stdio.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTextureFile::CTextureFile( bool editor )
{
        m_data                  = NULL;
        m_numData               = 0;
        m_height                = 0;
        m_width                 = 0;
        m_frames.x              = 0;
        m_frames.y              = 0;
        m_frames.rows   = 1; 
        m_frames.cols   = 1; 
        m_alpha                 = false;
        m_alphamap[0]   = 0;
        m_alphamap[1]   = 0;
        m_alphamap[2]   = 0;
        m_animNum               = 0;
        m_anim                  = NULL;
        m_collisionMap  = false;
        m_collisionData = NULL;
        m_numframes             = 0;
        m_framebounds   = NULL;

        m_editor                = editor;
}

CTextureFile::~CTextureFile()
{
        if ( m_data )
                delete[] m_data;

        if ( m_collisionData )
                delete[] m_collisionData;

        if ( m_anim )
        {
                for ( int i = 0; i < m_animNum; i++ )
                {
                        if ( m_anim[i].frames )
                                delete[] m_anim[i].frames;
                        if ( m_anim[i].name )
                                delete[] m_anim[i].name;
                }

                if ( m_editor )
                        free ( m_anim );
                else
                        delete[] m_anim;
        }

        //if ( m_framebounds )
        //      delete [] m_framebounds;
}

STextureData *CTextureFile::CreateData(int width, int height, STextureSize *size )
{
        if ( (!height) || (!width) )
                return NULL;

        size->height  = height;
        size->width   = width;
        size->num = (height * width);

        STextureData *data = new STextureData[size->num];

        for ( int i = 0; i < size->num; i++ )
        {
                data[i].r = 0;
                data[i].g = 0;
                data[i].b = 0;
                data[i].alpha = 255;
        }

        return data;
}

int CTextureFile::LoadTextureTGA(const char *filename )
{
        STextureSize *size = new STextureSize;

        int error;
        m_data = LoadTGA ( filename, size, &error );

        if ( error == TEXTURELOAD_OK )
        {
                m_height = size->height;
                m_width = size->width;
                m_numData = size->num;
        }
        else
        {
                delete [] m_data;
                m_data = NULL;
        }

        if ( m_collisionData )
                delete [] m_collisionData;
        m_collisionMap = false;

        delete [] size;

        return error;
}

int CTextureFile::LoadCollisionTGA(const char *filename )
{
        STextureSize *size = new STextureSize;

        int error;
        STextureData *data = LoadTGA ( filename, size, &error );

        if ( error == TEXTURELOAD_OK )
        {
                if ( (size->height != m_height) || (size->width != m_width) )
                        error = TEXTURELOAD_SIZEMISMATCH;
                else
                {
                        if ( m_collisionData )
                                delete [] m_collisionData;

                        m_collisionData = new bool[size->num];
                        for ( int i = 0; i < size->num; i++ )
                                m_collisionData[i] = (data[i].alpha) ? false : true;

                        m_collisionMap = true;
                }
        }


        delete [] data;
        delete [] size;

        return error;
}

bool CTextureFile::SaveTGA ( const char *filename )
{
        FILE *fileID = NULL;
        unsigned char header[18];
        unsigned char *imagedata;
        int i;
        int num;

#ifdef CY_USESECURE
        if ( fopen_s ( &fileID, filename, "wb" ) )
                return false;
#else
        fileID = fopen ( filename, "wb" );
        if ( !fileID )
                return false;
#endif

        for ( i = 0; i < 18; i++ ) //initilise header to all 0's
                header[i] = 0;

        // setup header
        header[2] = 2;
        header[12] = m_width % 256;
        header[13] = (int) (m_width / 256);
        header[14] = m_height % 256;
        header[15] = (int) (m_height / 256);

        header[16] = 32; // 32 bit
        int bitSize = header[16] / 8;

        fwrite(header, sizeof (unsigned char), 22, fileID);   // write header to the file

        imagedata = (unsigned char *)malloc(sizeof(unsigned char) * (m_width * m_height * bitSize));
        num = 0;
        // write colour data
        for ( i = 0; i < (m_width * m_height); i++)
        {
                imagedata[num]          = m_data[i].b;
                imagedata[num + 1]      = m_data[i].g;
                imagedata[num + 2]      = m_data[i].r;
                if ( bitSize == 4 )
                        imagedata[num + 3]      = (unsigned char) m_data[i].alpha;

                num += bitSize;
        }
        
        fwrite ( imagedata, sizeof (unsigned char), m_width * m_height * bitSize, fileID );
        fclose ( fileID );

        return true;
}

STextureData *CTextureFile::LoadTGA(const char *filename, STextureSize *tsize, int *error )
{
    unsigned char type[4];
    unsigned char info[7];
    unsigned char *imageData = NULL;
    int imageWidth, imageHeight;
    int imageBits, size;

        FILE *fileID;
        // open the filename
#ifdef CY_USESECURE
    if ( fopen_s ( &fileID, filename, "r+bt") )
        {
        *error = TEXTURELOAD_NOFILE;
                return NULL;
        }
#else
    if (!(fileID = fopen (filename, "r+bt")))
        {
        *error = TEXTURELOAD_NOFILE;
                return NULL;
        }
#endif

        // get the heading data
    fread (&type, sizeof (char), 3, fileID); // read in colormap info and image type
    fseek (fileID, 12, SEEK_SET);                       // seek past the header
    fread (&info, sizeof (char), 6, fileID);

        /*
    if (type[1] != 0 || (type[2] != 2 && type[2] != 3))
        {
                fclose ( fileID );
                return false;
        }*/

        // convert heading to data
    imageWidth = info[0] + info[1] * 256; // get image size from the header
    imageHeight = info[2] + info[3] * 256;
    imageBits = info[4]; 
    size = imageWidth * imageHeight;  // number of bits in image file

        // check for right format to load
    if ( (imageBits != 32) && (imageBits != 24) ) // invalid format
        {
                *error = TEXTURELOAD_INVALIDFORMAT;
                fclose ( fileID );
                return false;
        }

/*      if ( imageBits != 32 )
        {
                fclose ( fileID );
                *error = TEXTURELOAD_INVALIDFORMAT;
                return NULL;
        }*/

        int bitSize = (imageBits / 8);

        // read the data
    imageData = (unsigned char *)malloc (size * bitSize); 
    if (imageData == NULL)
        {
                fclose ( fileID );
                *error = TEXTURELOAD_INVALIDFORMAT;
                return NULL;
        }
    if ( (int)fread (imageData, sizeof (unsigned char), size * bitSize, fileID) != (int)(size * bitSize) )
        {
        free (imageData);
        *error = TEXTURELOAD_INVALIDFORMAT;
                return NULL;
    }

        // dont use greyscale ?

        // create the data storage
        STextureData *data = CreateData ( imageWidth, imageHeight, tsize );
        if ( !data )
        {
        free (imageData);
        *error = TEXTURELOAD_INVALIDFORMAT;
                return NULL;
        }

        // copy to data storage
        int tempI;
        for ( int i = 0; i < size; i++ )
        {
                tempI = i * bitSize;
                data[i].b = imageData[tempI];
                data[i].g = imageData[tempI + 1];
                data[i].r = imageData[tempI + 2];

                if ( imageBits == 32 )
                        data[i].alpha = imageData[tempI + 3];
        }
        
        free ( imageData );

    fclose (fileID);    

        *error = TEXTURELOAD_OK;

        return data;
}

#ifdef DIRECT3D
LPDIRECT3DTEXTURE9 CTextureFile::CreateTexture(LPDIRECT3DDEVICE9 device)
{
        if ( m_numData < 1 )
                return NULL;

        LPDIRECT3DTEXTURE9 texture;
        if ( FAILED (D3DXCreateTexture ( device, m_width, m_height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture )) )
                return NULL;

    D3DSURFACE_DESC surfDesc;
        texture->GetLevelDesc( 0, &surfDesc );

        int h = surfDesc.Height;

    D3DLOCKED_RECT LockedRect;
    texture->LockRect( 0, &LockedRect, NULL, 0 );

    DWORD* pBits;
    for( DWORD iTexelZ=0; iTexelZ < surfDesc.Height; iTexelZ++ )
    {
        pBits = (DWORD*) ((BYTE*)LockedRect.pBits + (LockedRect.Pitch * iTexelZ));

        for( DWORD iTexelX = 0; iTexelX < surfDesc.Width; iTexelX++ )
        {
                        int num = m_numData - ((iTexelZ * surfDesc.Width) + (surfDesc.Width - iTexelX));

            DWORD nRed   = (DWORD)  m_data[num].r;
            DWORD nGreen = (DWORD)  m_data[num].g;
            DWORD nBlue  = (DWORD)  m_data[num].b;
            DWORD nAlpha = (DWORD)  m_data[num].alpha;

                        if ( m_alpha )
                        {
                                if ( (m_data[num].r == m_alphamap[0]) && (m_data[num].g == m_alphamap[1]) && (m_data[num].b == m_alphamap[2]) )
                                        nAlpha = 0;
                        }

            if( nRed > 0xFF )
                nRed = 0xFF;
            if( nGreen > 0xFF )
                nGreen = 0xFF;
            if( nBlue > 0xFF )
                nBlue = 0xFF;
            if( nAlpha > 0xFF )
                nAlpha = 0xFF;

            *pBits = (nAlpha << 24) + (nRed << 16) + (nGreen << 8) + (nBlue << 0);
            pBits++;
                }
        }

        texture->UnlockRect ( 0 );

        return texture;
}
LPDIRECT3DTEXTURE9 CTextureFile::CreateCollisionTexture(LPDIRECT3DDEVICE9 device, DWORD nRed, DWORD nGreen, DWORD nBlue)
{
        if ( (m_numData < 1) || (!m_collisionMap) || (!m_collisionData) )
                return NULL;

        LPDIRECT3DTEXTURE9 texture;
        if ( FAILED (D3DXCreateTexture ( device, m_width, m_height, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture )) )
                return NULL;

    D3DSURFACE_DESC surfDesc;
        texture->GetLevelDesc( 0, &surfDesc );

        int h = surfDesc.Height;

    D3DLOCKED_RECT LockedRect;
    texture->LockRect( 0, &LockedRect, NULL, 0 );

    DWORD* pBits;
    for( DWORD iTexelZ=0; iTexelZ < surfDesc.Height; iTexelZ++ )
    {
        pBits = (DWORD*) ((BYTE*)LockedRect.pBits + (LockedRect.Pitch * iTexelZ));

        for( DWORD iTexelX = 0; iTexelX < surfDesc.Width; iTexelX++ )
        {
                        int num = m_numData - ((iTexelZ * surfDesc.Width) + (surfDesc.Width - iTexelX));

                        DWORD nAlpha = 0xFF;
                        if ( m_collisionData[num] )
                                nAlpha = 0x00;

            if( nRed > 0xFF )
                nRed = 0xFF;
            if( nGreen > 0xFF )
                nGreen = 0xFF;
            if( nBlue > 0xFF )
                nBlue = 0xFF;
            if( nAlpha > 0xFF )
                nAlpha = 0xFF;

            *pBits = (nAlpha << 24) + (nRed << 16) + (nGreen << 8) + (nBlue << 0);
            pBits++;
                }
        }

        texture->UnlockRect ( 0 );

        return texture;
}
#endif

bool CTextureFile::Save(const char *filename)
{
        // create header
        SFileHeader header;

        ClearHeader ( &header );


        // open file
        FILE *id;
#ifdef CY_USESECURE
        if ( fopen_s ( &id, filename, "wb" )  )
                return false;
#else
        if ( (id = fopen ( filename, "wb" )) == NULL )
                return false;
#endif

        // write version numbers (used incase header needs to change in later versions)
        float version = (float) TEXTUREFILE_VERSION;
        fwrite ( &version, sizeof ( float ), 1, id );

        // write header to file
        header.width            = m_width;
        header.height           = m_height;
        header.frames           = false;
        header.alpha            = m_alpha;
        header.alphamap[0]      = m_alphamap[0];
        header.alphamap[1]      = m_alphamap[1];
        header.alphamap[2]      = m_alphamap[2];
        if ( m_animNum )
                header.frames = true;

        fwrite ( &header, sizeof ( SFileHeader ), 1, id );

        // * Added since verison 1.20
        fwrite ( &m_collisionMap, sizeof ( bool ), 1, id );

        // write all the data
        fwrite ( m_data, sizeof ( STextureData ), m_numData, id );

        // write frames data
        fwrite ( &m_frames, sizeof ( STextureFrames ), 1, id );

        int animNum = 0;
        int i;
        for ( i = 0; i < m_animNum; i++ )
        {
                if ( !m_anim[i].remove )
                        animNum++;
        }
        fwrite ( &animNum, sizeof ( int ), 1, id );

        SAnimation *anim;
        for ( i = 0; i < m_animNum; i++ )
        {
                anim = &m_anim[i];

                if ( !anim->remove )
                {
                        int size = (int)strlen ( anim->name ) + 1;
                        fwrite ( &size, sizeof ( int ), 1, id );
                        fwrite ( anim->name, sizeof ( char ), size, id );

                        fwrite ( &anim->amt, sizeof ( int ), 1, id );
                        fwrite ( &anim->delay, sizeof ( int ), 1, id );
                        fwrite ( &anim->cont, sizeof ( bool ), 1, id );

                        fwrite ( anim->frames, sizeof ( int ), anim->amt, id );
                }
        }

        if ( m_collisionMap )
                fwrite ( m_collisionData, sizeof ( bool ), m_numData, id );

        fclose ( id );

        return true;
}

void CTextureFile::ClearHeader(SFileHeader *header)
{
        header->alpha = false;
        header->alphamap[0] = 0;
        header->alphamap[1] = 0;
        header->alphamap[2] = 0;

        header->frames = false;
        header->height = 0;
        header->width = 0;
}

bool CTextureFile::Load(const char *filename)
{
        // create header
        SFileHeader header;
        ClearHeader ( &header );

        // open file
        FILE *id;
#ifdef CY_USESECURE
        if ( fopen_s ( &id, filename, "rb" ) )
                return false;
#else
        if ( (id = fopen ( filename, "rb" )) == NULL )
                return false;
#endif


        // read version numbers (used incase header needs to change in later versions)
        float version;
        fread ( &version, sizeof ( float ), 1, id );

        if ( version < 1.00 )
                return false;

        // read header from file
        fread ( &header, sizeof ( SFileHeader ), 1, id );
        m_width                 = header.width;
        m_height                = header.height;
        m_numData               = m_width * m_height;
        m_alpha                 = header.alpha;
        m_alphamap[0]   = header.alphamap[0];
        m_alphamap[1]   = header.alphamap[1];
        m_alphamap[2]   = header.alphamap[2];
        m_alpha                 = header.alpha;

        // * Added since version 1.20
        if ( version > 1.20 )
                fread ( &m_collisionMap, sizeof ( bool ), 1, id );

        // read all the data
        m_data = new STextureData[m_numData];   
        
        if ( fread ( m_data, sizeof ( STextureData ), m_numData, id ) != m_numData )
                return false;


        fread ( &m_frames, sizeof ( STextureFrames ), 1, id );

        if ( (version >= 1.1) && (header.frames) )
        {
                // read number of animations
                int fnum, size;
                fread ( &fnum, sizeof ( int ), 1, id );

                if ( m_anim )
                {
                        if ( m_editor )
                                free ( m_anim );
                        else
                                delete []m_anim;
                }

                if ( !fnum )
                        m_anim = NULL;
                else
                {
                        if ( m_editor )
                                m_anim = (SAnimation *)malloc(sizeof ( SAnimation ) * fnum);
                        else
                                m_anim = new SAnimation[fnum];
                }

                SAnimation *anim;
                for ( int i = 0; i < fnum; i++ )
                {
                        anim = &m_anim[i];

                        fread ( &size, sizeof ( int ), 1, id );
                        anim->name = new char[size];
                        fread ( anim->name, sizeof ( char ), size, id );

                        fread ( &anim->amt, sizeof ( int ), 1, id );
                        fread ( &anim->delay, sizeof ( int ), 1, id );
                        fread ( &anim->cont, sizeof ( bool ), 1, id );
                        anim->remove = false;
                        anim->frames = new int[anim->amt];
                        fread ( anim->frames, sizeof ( int ), anim->amt, id );
                }
                m_animNum = fnum;
        }

        if ( m_collisionMap )
        {
                m_collisionData = new bool[m_numData];  
        
                if ( fread ( m_collisionData, sizeof ( bool ), m_numData, id ) != m_numData )
                        return false;
        }

        fclose ( id );

        m_numframes = m_frames.x * m_frames.y;
        m_framebounds = new SFrameBounds[m_numframes];

        for ( int i = 0; i < m_numframes; i++ )
        {
                m_framebounds[i].minx = ComputeMinAlphaX ( i );
                m_framebounds[i].maxx = ComputeMaxAlphaX ( i );
                m_framebounds[i].miny = ComputeMinAlphaY ( i );
                m_framebounds[i].maxy = ComputeMaxAlphaY ( i );
        }

        return true;
}

void CTextureFile::SetFrameSize(int width, int height)
{
        m_frames.x = width;
        m_frames.y = height;
}

const STextureFrames *CTextureFile::GetFrames()
{
        return &m_frames;
}

int CTextureFile::GetFrameWidth()
{
        return m_frames.x;
}

int CTextureFile::GetFrameHeight()
{

        return m_frames.y;
}

void CTextureFile::SetFrames(int rows, int cols)
{
        m_frames.rows = rows;
        m_frames.cols = cols;
}

void CTextureFile::EnableAlphamap(bool enable)
{
        m_alpha = enable;
}

bool CTextureFile::GetAlphaMap()
{
        return m_alpha;
}

void CTextureFile::SetAlphaMap(int red, int green, int blue)
{
        m_alphamap[0] = red;
        m_alphamap[1] = green;
        m_alphamap[2] = blue;
}

bool CTextureFile::GetAlphaMap(int *red, int *green, int *blue)
{
        *red   = m_alphamap[0];
        *green = m_alphamap[1];
        *blue  = m_alphamap[2];

        return m_alpha;
}

SAnimation * CTextureFile::AddNewAnimation(const char *name)
{
        SAnimation *anim = NULL;
        // first, find if theres any free slots
        for ( int i = 0; i < m_animNum; i++ )
        {
                // found a free slot
                if ( m_anim[i].remove )
                {
                        anim = &m_anim[i];

                        // make sure old data is cleared before using
                        if ( anim->frames )
                                delete []anim->frames;
                        if ( anim->name )
                                delete []anim->name;
                        break;
                }
        }

        // no free slot ? add a new one to the end of the array
        if ( !anim )
        {
                ++m_animNum;
                m_anim = (SAnimation *)realloc(m_anim, sizeof ( SAnimation ) * m_animNum);

                anim = &m_anim[m_animNum - 1];
        }

        // set default data for animation
        anim->amt = 1;
        anim->frames = new int[1];
        anim->remove = false;
        anim->frames[0] = 1;
        anim->cont = false;
        anim->delay = 1;
        anim->name = new char[strlen (name) + 1];
#ifdef CY_USESECURE
        strcpy_s ( anim->name, strlen (name) + 1, name );
#else
        strcpy ( anim->name, name );
#endif

        // return pointer to created animation
        return anim;
}

int CTextureFile::GetAnimationNum()
{
        return m_animNum;
}       

SAnimation * CTextureFile::FindAnimation(const char *search)
{
        Utils::String sSearch(search);

        for ( int i = 0; i < m_animNum; i++ ) {
                if ( !m_anim[i].remove ) {
                        if ( sSearch.Compare(m_anim[i].name) ) {
                                return &m_anim[i];
                        }
                }
        }

        return NULL;
}

SAnimation * CTextureFile::GetAnimation(int num)
{
        if ( (m_animNum < num) || (num < 0) )
                return NULL;

        return &m_anim[num];
}

void CTextureFile::SetEditor()
{
        m_editor = true;
}


void IncAnimationFrame ( SPlayAnimation *anim, int *times )
{
        IncAnimationFrame ( anim, false, times );
}
void IncAnimationFrame ( SPlayAnimation *anim, bool endstop, int *times )
{
        if ( !anim )
                return;

        if ( !anim->anim )
                return;

        ++anim->delay;

        if ( anim->delay >= anim->anim->delay )
        {
                anim->delay = 0;
                ++anim->curframe;

                if ( anim->curframe > anim->anim->amt )
                {
                        if ( anim->anim->cont )
                        {
                                anim->curframe = 1;
                                if ( times )
                                        ++(*times);
                        }
                        else if ( endstop )
                                --anim->curframe;
                        else
                                anim->anim = NULL;
                }
        }
}
void DecAnimationFrame ( SPlayAnimation *anim, int *times )
{
        DecAnimationFrame ( anim, false, times );
}
void DecAnimationFrame ( SPlayAnimation *anim, bool endstop, int *times )
{
        if ( !anim )
                return;

        if ( !anim->anim )
                return;

        ++anim->delay;

        if ( anim->delay >= anim->anim->delay )
        {
                anim->delay = 0;
                --anim->curframe;

                if ( anim->curframe < 1 )
                {
                        if ( anim->anim->cont )
                        {
                                anim->curframe = anim->anim->amt;
                                if ( times )
                                        ++(*times);
                        }
                        else if ( endstop )
                                anim->curframe = 1;
                        else
                                anim->anim = NULL;
                }
        }
}
void ResetAnimation ( SPlayAnimation *anim )
{
        if ( !anim )
                return;

        anim->anim = NULL;
        anim->delay = 0;
        anim->curframe = 1;
        anim->reverse = false;
}


void DoAnimation ( SPlayAnimation *anim, bool endstopinc, bool endstopdec, int *times )
{
        if ( !anim )
                return;

        if ( anim->reverse )
                DecAnimationFrame ( anim, endstopdec, times );
        else
                IncAnimationFrame ( anim, endstopinc, times );
}

bool CTextureFile::CheckAlpha(int x, int y, int frame, bool inccollisionmap)
{
        int row = ((frame - 1) / m_frames.cols) + 1;
        int col = frame % m_frames.cols;
        if ( col == 0)
                col = m_frames.cols;

        int newx = ((col - 1) * m_frames.x) + x;
        int newy = ((row - 1) * m_frames.y) + y;

        int num = (newy * m_width) + newx;
        if ( (num < 0) || (num > (m_width * m_height)) )
                return false;

        if ( (inccollisionmap) && (m_collisionMap) && (m_collisionData) )
                return m_collisionData[num];

        int r = m_data[num].r;
        int g = m_data[num].g;
        int b = m_data[num].b;
        int a = m_data[num].alpha;

        if ( m_data[num].alpha == 0 )
                return true;

        if ( m_alpha )
        {
                if ( (m_data[num].r == m_alphamap[0]) && (m_data[num].g == m_alphamap[1]) && (m_data[num].b == m_alphamap[2]) )
                        return true;
        }

        return false;
}

std::string CTextureFile::GetErrorText ( int error )
{
        switch ( error )
        {
                case TEXTURELOAD_OK:
                        return "";
                case TEXTURELOAD_NOFILE:
                        return "unable to open file";
                case TEXTURELOAD_SIZEMISMATCH:
                        return "size mismatch";
                case TEXTURELOAD_INVALIDFORMAT:
                        return "Invalid Format";
        }
        return "Unknown Error";
}


int CTextureFile::ComputeMinAlphaX ( int frame )
{
        for ( unsigned int x = 0; x < m_frames.x; x++ )
        {
                for ( unsigned int y = 0; y < m_frames.y; y++ )
                {
                        if ( !CheckAlpha ( x, y, frame ) )
                                return x;

                }
        }
        return 0;
}

int CTextureFile::ComputeMaxAlphaX ( int frame )
{
        for ( unsigned int x = m_frames.x - 1; x >= 0; x-- )
        {
                for ( unsigned int y = 0; y < m_frames.y; y++ )
                {
                        if ( !CheckAlpha ( x, y, frame ) )
                                return x;

                }
        }
        return m_frames.x - 1;
}

int CTextureFile::ComputeMinAlphaY ( int frame )
{
        for ( unsigned int y = 0; y < m_frames.y; y++ )
        {
                for ( unsigned int x = 0; x < m_frames.x; x++ )
                {
                        if ( !CheckAlpha ( x, y, frame ) )
                                return y;

                }
        }
        return 0;
}

int CTextureFile::ComputeMaxAlphaY ( int frame )
{
        for ( unsigned int y = m_frames.y - 1; y >= 0; y-- )
        {
                for ( unsigned int x = 0; x < m_frames.x; x++ )
                {
                        if ( !CheckAlpha ( x, y, frame ) )
                                return y;

                }
        }
        return m_frames.y - 1;
}