/*****************************************************************************/
/* Copyright (c) 2008 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/**
 *      \file           phStbGif.c
 *
 *      \brief          Gif decoder component.
 */
/*
 Rev Date       Author      Comments
--------------------------------------------------------------------------------
  1 20060327    Mike Neill  Original
  2 20060405    Mike Neill  Performance optimisations
  3 20061009    Mike Neill  More performance optimisations
  4 20061117    Mike Neill  Make library re-entrant
  5 20070320    Mike Neill  Changes after QAC checking
  6 20071023    Mike Neill  More QAC checking
  8 20080208    batelaan    Update file copyright/license text
  9 20080718    Mike Neill  Fix animated GIFs with no delay time
 10 20081219    Mike Neill  Add support for multiple graphics modes
 11 20090116    neill       Add support for YUV format output
--------------------------------------------------------------------------------
*/

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/time.h>

#include "phStbGif.h"
#include "phStbImageUtils.h"

/***********************************************************
 * Types and defines:
 **********************************************************/

/***********************************************************
 * Global data:
 **********************************************************/

/***********************************************************
 * Internal Functions:
 **********************************************************/

/******************* 
* LOCAL MACROS     *
********************/

#define PH_STB_GIF_INPUT_BUFFER_SIZE     (16*1024)

#define PH_STB_GIF_EXTENSION_HEADER_BYTE (0x21)
#define PH_STB_GIF_FULL_ALPHA            (0xFF000000u)
#define PH_STB_GIF_TRANSPARENT_MASK      (0x00FFFFFF)
#define PH_STB_GIF_COLOUR_MAP_MASK       (0x80)
#define PH_STB_GIF_INTERLACED_MASK       (0x40)
#define PH_STB_GIF_COLOUR_DEPTH_MASK     (0x07)
#define PH_STB_GIF_TERMINATOR            (0x3B)
#define PH_STB_GIF_TRANSPARENCY_CODE     (0xF9)
#define PH_STB_GIF_TRANSPARENCY_MASK     (0x01)
#define PH_STB_GIF_UNDRAW_MASK           (0x1C)
#define PH_STB_GIF_RESTORE_BACKGROUND    (0x08)
#define PH_STB_GIF_CODE_SIZE_LIMIT       (12)
#define PH_STB_GIF_MAX_TABLE_SIZE        (4096)
#define PH_STB_GIF_COLOUR_MAP_SIZE       (256)
#define PH_STB_GIF_BIT_BUFFER_SIZE       (256)
#define PH_STB_GIF_SIGNATURE_LENGTH      (6)

#define PH_STB_GIF_INVALID_CODE          (0xFFFFFFFFu)

#define PH_STB_GIF_GET_BYTE_VALUE(pContext, a) \
    (pContext)->getByteVariable = phStbGif_getByteValue((pContext), (a)); \
    if ((pContext)->getByteVariable==-1) \
    { \
        return PH_STB_ERR_FILE_TRUNCATED; \
    }

#define PH_STB_GIF_CHECK(a) \
{ \
    phStbImageErrorCode_t err; \
    err = (a); \
    if (err != PH_STB_OK) \
    { \
        return err; \
    } \
}

#define PH_STB_GIF_CHECK_LABEL(a, b) \
{ \
    status = (a); \
    if (status != PH_STB_OK) \
    { \
        DBG_PRINT((dbgGif,DBG_DEFAULT,"%s : Error at line %d\n", __FUNCTION__, __LINE__)); \
        goto b; \
    } \
}

#define PH_STB_GIF_CHECK_AND_STOP(a) \
{ \
    status = (a); \
    if (status != PH_STB_OK) \
    { \
        DBG_PRINT((dbgGif,DBG_DEFAULT,"%s : Error at line %d\n", __FUNCTION__, __LINE__)); \
        moreToProcess = 0; \
    } \
}

/******************* 
* LOCAL TYPEDEFS   *
********************/

typedef struct _phStbGif_Input_t
{
    int32_t   file;
    uint8_t   inputBuffer[PH_STB_GIF_INPUT_BUFFER_SIZE];
    uint32_t  noOfBytesLeft;    /* No of bytes left in the Buffer, ie. packet */
    uint8_t * pCurrentByte;     /* pointer to the current byte accessed from the buffer, ie. packet*/
}phStbGif_Input_t, * pphStbGif_Input_t;

typedef struct
{
   uint32_t previous;
   uint32_t first;
   uint32_t output;
   int32_t length;
} phStbGif_DataEntry_t;

typedef struct
{
   int32_t width;
   int32_t height;
   int32_t backgroundColour;
   int32_t aspectRatio;
   int32_t colourDepth;
   uint32_t flags;
} phStbGif_GlobalProperties_t;

typedef struct
{
   int32_t header;
   int32_t positionX;
   int32_t positionY;
   int32_t width;
   int32_t height;
   int32_t colourDepth;
   uint32_t flags;
   int32_t interlaced;
   int32_t decodePass;
   int32_t outputX;
   int32_t outputY;
} phStbGif_LocalProperties_t;

typedef struct
{
    phStbGif_Input_t pGifInput;
    
    /* Variables to hold the global and local image properties */
    phStbGif_GlobalProperties_t globalProperties;
    phStbGif_LocalProperties_t  localProperties;
    
    /* Variables used to obtain data bytes */
    uint8_t putByte;
    bool    usePutByte;
    int32_t getByteVariable;
    
    /* Variables used to obtain variable bit length data codes */
    uint32_t bitData[PH_STB_GIF_BIT_BUFFER_SIZE];
    int32_t currentBit;
    int32_t currentByte;
    int32_t maxBytes;
    bool    dataOK;
    
    /* Variables used to hold the colour map information */
    uint32_t globalColourMap[PH_STB_GIF_COLOUR_MAP_SIZE];
    uint32_t localColourMap[PH_STB_GIF_COLOUR_MAP_SIZE];
    bool     hasTransparency;
    int32_t  transparentColour;
    uint32_t duration;
    bool     doFillBackground;
    
    /* Pointer to the decode buffer */
    uint32_t * dataBuffer;
    
    /* Variables used to keep track of the decoded pixel sequences */
    phStbGif_DataEntry_t pixelLookup[PH_STB_GIF_MAX_TABLE_SIZE];
    uint32_t         thisCode;
    uint32_t         lastCode;
    int32_t          codeSize;
    int32_t          initialCodeSize;
    uint32_t         nextAvailable;
    uint32_t         pixelOutput[PH_STB_GIF_MAX_TABLE_SIZE];
} phStbGif_contextProperties_t;

/******************* 
* STATIC DATA      *
********************/

static const uint32_t phStbGif_shiftMask[] = { 0x00000001u, 0x00000002u, 0x00000004u, 0x00000008u, 0x00000010u, 0x00000020u, 0x00000040u, 0x00000080u,
                                                   0x00000100u, 0x00000200u, 0x00000400u, 0x00000800u, 0x00001000u, 0x00002000u, 0x00004000u, 0x00008000u,
                                                   0x00010000u, 0x00020000u, 0x00040000u, 0x00080000u, 0x00100000u, 0x00200000u, 0x00400000u, 0x00800000u,
                                                   0x01000000u, 0x02000000u, 0x04000000u, 0x08000000u, 0x10000000u, 0x20000000u, 0x40000000u, 0x80000000u };

/********************** 
* FUNCTION PROTOTYPES *
***********************/

/*******************************************************************************
*@begin
* NAME: phStbGif_FillBuffer
*       Local function used to refill the input buffer
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/

static phStbImageErrorCode_t phStbGif_FillBuffer(phStbGif_contextProperties_t* pContext)
{
    int32_t bytesToRead;
    int32_t bytesRead;
    bytesToRead                       = PH_STB_GIF_INPUT_BUFFER_SIZE;
    pContext->pGifInput.noOfBytesLeft = 0;
    pContext->pGifInput.pCurrentByte  = pContext->pGifInput.inputBuffer;

    do
    {
        bytesRead = read(pContext->pGifInput.file, &(pContext->pGifInput.inputBuffer[pContext->pGifInput.noOfBytesLeft]), (uint32_t)bytesToRead);

        if (bytesRead < 0) 
        {
            return PH_STB_ERR_FILE_TRUNCATED;
        }

        pContext->pGifInput.noOfBytesLeft += (uint32_t)bytesRead;

        if (bytesRead == 0) 
        {
            if (pContext->pGifInput.noOfBytesLeft!=0) 
            {
                return PH_STB_OK;
            }
            return PH_STB_ERR_FILE_TRUNCATED;
        }

        bytesToRead -= bytesRead;

    }while(bytesToRead);

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readFunc
*       Replacement read function used during the GIF decoding
*       
* Returns : None
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* data          OUT     Pointer to output data
* length        IN      Length of output data
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/

static phStbImageErrorCode_t phStbGif_readFunc(phStbGif_contextProperties_t* pContext, uint8_t* data, uint32_t length)
{
    if (pContext->pGifInput.noOfBytesLeft>=length)
    {
        /* Enough bytes in buffer to fill GIF buffer */
        (void)memcpy(data, pContext->pGifInput.pCurrentByte, length);
        pContext->pGifInput.pCurrentByte  += length;
        pContext->pGifInput.noOfBytesLeft -= length;
    }
    else
    {
        /* Not enough bytes in buffer to fill GIF buffer */
        (void)memcpy(data, pContext->pGifInput.pCurrentByte, pContext->pGifInput.noOfBytesLeft);
        length -= pContext->pGifInput.noOfBytesLeft;
        data += pContext->pGifInput.noOfBytesLeft;

        while(length > 0)
        {
            if (phStbGif_FillBuffer(pContext)!=PH_STB_OK)
            {
                /* Error while trying to get more data - give up quick! */
                return PH_STB_ERR_FILE_TRUNCATED;
            }
            if (pContext->pGifInput.noOfBytesLeft>=length)
            {
                /* Enough bytes in buffer to fill GIF buffer */
                (void)memcpy(data, pContext->pGifInput.pCurrentByte, length);
                pContext->pGifInput.pCurrentByte  += length;
                pContext->pGifInput.noOfBytesLeft -= length;
                length = 0;
            }
            else
            {
                (void)memcpy(data, pContext->pGifInput.pCurrentByte, pContext->pGifInput.noOfBytesLeft);
                length -= pContext->pGifInput.noOfBytesLeft;
                data += pContext->pGifInput.noOfBytesLeft;
            }
        }
    }
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_initLookup
*       Initialises the the pixel sequence lookup table
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* colourDepth   IN      Colour depth of image
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static void phStbGif_initLookUp(phStbGif_contextProperties_t* pContext, uint32_t colourDepth)
{
   uint32_t i;
   for(i=0; i<PH_STB_GIF_MAX_TABLE_SIZE; i++)
   {
      if (i<colourDepth)
      {
         pContext->pixelLookup[i].previous = PH_STB_GIF_INVALID_CODE;
         pContext->pixelLookup[i].first = i;
         pContext->pixelLookup[i].output = i;
         pContext->pixelLookup[i].length = 1;
      }
      else
      {
         pContext->pixelLookup[i].previous = PH_STB_GIF_INVALID_CODE;
         pContext->pixelLookup[i].first = PH_STB_GIF_INVALID_CODE;
         pContext->pixelLookup[i].output = PH_STB_GIF_INVALID_CODE;
         pContext->pixelLookup[i].length = -1;
      }
   }
}

/*******************************************************************************
*@begin
* NAME: phStbGif_convertLocalToGlobalPosition
*       Converts a local pixel position to a global pixel position
*       
* Returns : Pointer to the pixel location within the output buffer
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static uint32_t *phStbGif_convertLocalToGlobalPosition(phStbGif_contextProperties_t const * pContext)
{
    int32_t globalX;
    int32_t globalY;

    globalX = pContext->localProperties.positionX+pContext->localProperties.outputX;
    globalY = pContext->localProperties.positionY+pContext->localProperties.outputY;

    /* Clip */
    CLAMP_VALUE(globalX, (pContext->globalProperties.width-1))
    CLAMP_VALUE(globalY, (pContext->globalProperties.height-1))
    return pContext->dataBuffer+(globalY*pContext->globalProperties.width)+globalX;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_FillBackground
*       Local function used to fill the image with the background colour
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* pColourMap    IN      Pointer to colour map (Palette)
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/

static void phStbGif_fillBackground(phStbGif_contextProperties_t const * pContext, uint32_t const * pColourMap)
{
    int32_t i;
    uint32_t *ptr = pContext->dataBuffer;
    for(i=0; i<(pContext->globalProperties.width*pContext->globalProperties.height); i++)
    {
        *ptr = pColourMap[pContext->globalProperties.backgroundColour];
        ptr++;
    }
}

/*******************************************************************************
*@begin
* NAME: phStbGif_outputPixel
*       Writes a single pixel value to the output buffer
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* pColourMap    IN      Pointer to colour map (Palette)
* which         IN      Index into the colour map
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbGif_outputPixel(phStbGif_contextProperties_t* pContext, uint32_t const * pColourMap, uint32_t which)
{
    uint32_t *pOutput;

    pOutput = phStbGif_convertLocalToGlobalPosition(pContext);

    if (pColourMap[which]&PH_STB_GIF_FULL_ALPHA)
    {
        *pOutput = pColourMap[which];
    }
    pContext->localProperties.outputX++;
    if (pContext->localProperties.outputX >= pContext->localProperties.width)
    {
        pContext->localProperties.outputX = 0;
        /* Check for interlaced GIF images */
        if (pContext->localProperties.interlaced)
        {
            switch(pContext->localProperties.decodePass)
            {
                case(2) : pContext->localProperties.outputY += 4;
                          break;
                case(3) : pContext->localProperties.outputY += 2;
                          break;
                default : pContext->localProperties.outputY += 8;
                          break;
            }
            if (pContext->localProperties.outputY >= pContext->localProperties.height)
            {
                pContext->localProperties.decodePass++;
                switch(pContext->localProperties.decodePass)
                {
                    case(1) : pContext->localProperties.outputY = 4;
                              break;
                    case(2) : pContext->localProperties.outputY = 2;
                              break;
                    case(3) : pContext->localProperties.outputY = 1;
                              break;
                    default : pContext->localProperties.outputY = 0;
                              break;
                }
            }
        }
        else
        {
            pContext->localProperties.outputY++;
        }
    }
}

/*******************************************************************************
*@begin
* NAME: phStbGif_addEntry
*       Adds a pixel sequence entry to the lookup table
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* which         IN      ID of the new entry to be created
* sourceEntry   IN      ID of the entry to be used as the basis for the new entry
* append        IN      pixel value to be appended to the start of the source entry
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbGif_addEntry(phStbGif_contextProperties_t* pContext, uint32_t which, uint32_t sourceEntry, uint32_t append)
{
   pContext->pixelLookup[which].previous = sourceEntry;
   pContext->pixelLookup[which].first = pContext->pixelLookup[sourceEntry].first;
   pContext->pixelLookup[which].output = pContext->pixelLookup[pContext->pixelLookup[append].first].output;
   pContext->pixelLookup[which].length = pContext->pixelLookup[sourceEntry].length+1;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_getByteValue
*       Gets multiple bytes of data from the input stream
*       
* Returns : Data bytes shifted into a single 32 bit value
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* bytes         IN      Number of data bytes to be read
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static int32_t phStbGif_getByteValue(phStbGif_contextProperties_t* pContext, int32_t bytes)
{
   int32_t i;
   uint32_t value = 0;
   uint8_t buffer[1];

   for(i=0; i<bytes; i++)
   {
      /* Check to see if there is a returned byte to be used */
      if (pContext->usePutByte)
      {
          buffer[0] = pContext->putByte;
          pContext->usePutByte = false;
      }
      else
      {
          if (phStbGif_readFunc(pContext, buffer, 1)!=PH_STB_OK)
          {
              return -1;
          }
      }
      value |= ((uint32_t)buffer[0]<<(i*8));
   }
   return (int32_t)value;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_putByteValue
*       Returns one byte of data to the read buffer
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* byte          IN      Data byte to be returned to the buffer
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static void phStbGif_putByteValue(phStbGif_contextProperties_t* pContext, uint8_t byte)
{
   pContext->putByte = byte;
   pContext->usePutByte = true;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_getMoreData
*       Refills the bit code data buffer with the next section of GIF data
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* pLength   OUT Number of bytes read in
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_getMoreData(phStbGif_contextProperties_t* pContext, int32_t * pLength)
{
   *pLength = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   if (*pLength!=0)
   {
      int32_t i;
      for(i=0; i<*pLength; i++)
      {
         int32_t value;
         value = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
         pContext->bitData[i] = (uint32_t)value;
      }
      pContext->maxBytes = *pLength;
      pContext->currentByte = 0;
      pContext->dataOK = true;
   }
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_getBitValue
*       Returns the next compressed code
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* bits          IN      Number of bit of data in the code 
* pValue        OUT     output value
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_getBitValue(phStbGif_contextProperties_t* pContext, int32_t bits, uint32_t * pValue)
{
   const uint32_t phStbGif_bitMask[] = { 0x01u, 0x02u, 0x04u, 0x08u, 0x10u, 0x20u, 0x40u, 0x80u };
   const uint32_t phStbGif_andMask[] = { 0x00u, 0x01u, 0x03u, 0x07u, 0x0Fu, 0x1Fu, 0x3Fu, 0x7Fu, 0xFFu };
   int32_t i;
   int32_t bitIncrement;
   *pValue = 0;
   
   for(i=0; i<bits; i+=bitIncrement)
   {
      int32_t remainingBits = bits - i;
      bitIncrement = 1;
      if (pContext->currentBit==0)
      {
          pContext->currentByte++;
          if (pContext->currentByte == pContext->maxBytes)
          {
              int32_t length;
              PH_STB_GIF_CHECK(phStbGif_getMoreData(pContext, &length))
              if (length==0)
              {
                  pContext->dataOK = false;
                  return PH_STB_OK;
              }
          }
      }

      if (pContext->currentBit == 0)
      {
          if (remainingBits <= 8)
          {
              *pValue |= (pContext->bitData[pContext->currentByte] & phStbGif_andMask[remainingBits]) << i;
              pContext->currentBit = remainingBits%8;
              return PH_STB_OK;
          }
          else
          {
              *pValue |= pContext->bitData[pContext->currentByte] << i;
              bitIncrement+=7;
          }
      }
      else
      {
          if (pContext->bitData[pContext->currentByte]&phStbGif_bitMask[pContext->currentBit])
          {
             *pValue |= phStbGif_shiftMask[i];
          }
          pContext->currentBit++;
      }
      pContext->currentBit %= 8;
   }
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_decodeSequence
*       Goes through the lookup table to produce a sequence of pixel codes
*       
* Returns : pointer to the start of the list of pixel codes
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* code          IN      Code to decode the pixel sequence for
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline uint32_t * phStbGif_decodeSequence(phStbGif_contextProperties_t* pContext, uint32_t code)
{
   int32_t i;
   uint32_t which = code;
   for(i=0; i<pContext->pixelLookup[code].length; i++)
   {
       pContext->pixelOutput[pContext->pixelLookup[code].length-i-1] = pContext->pixelLookup[which].output;
       which = pContext->pixelLookup[which].previous;
   }
   return pContext->pixelOutput;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_resetCodeSize
*       Resets the decoder back to the initial settings
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* colourDepth   IN      Colour depth of image
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static void phStbGif_resetCodeSize(phStbGif_contextProperties_t* pContext, uint32_t colourDepth)
{
    pContext->codeSize = pContext->initialCodeSize;
    pContext->nextAvailable = phStbGif_shiftMask[pContext->initialCodeSize]+2;
    phStbGif_initLookUp(pContext, colourDepth);
    pContext->thisCode = PH_STB_GIF_INVALID_CODE;
    pContext->lastCode = PH_STB_GIF_INVALID_CODE;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_decodeRasterBlock
*       Decodes a block of pixel information
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* colourDepth   IN      Colour depth of image
* pColourMap    IN      Pointer to colour map (Palette)
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_decodeRasterBlock(phStbGif_contextProperties_t* pContext, uint32_t colourDepth, uint32_t const * pColourMap)
{
    while(1)
    {
        /* Read in the next compressed data code */
        PH_STB_GIF_CHECK(phStbGif_getBitValue(pContext, pContext->codeSize+1, &(pContext->thisCode)))

        /* Check that there was enough data to continue */
        if (pContext->dataOK)
        {

            /* Check to see if the code is the 'ClearCode' */
            if (pContext->thisCode == phStbGif_shiftMask[pContext->initialCodeSize])
            {
                /* Reset the lookup table to its original values */
                phStbGif_resetCodeSize(pContext, colourDepth);
            }
            else
            /* Check to see if the code is a 'FinishCode' */
            if (pContext->thisCode == phStbGif_shiftMask[pContext->initialCodeSize]+1)
            {
                /* Finish this block of pixels */
                return PH_STB_OK;
            }
            else
            {
                /* Check to see if this is the first code that we have read in */
                if (pContext->lastCode==PH_STB_GIF_INVALID_CODE)
                {
                    /* Output the pixel associated with this code */
                    phStbGif_outputPixel(pContext, pColourMap, pContext->thisCode);
                }
                else
                {
                    uint32_t * sequence;
                    int32_t i;
                    /* Check to see if the code is in the data dictionary */
                    if (pContext->thisCode>=pContext->nextAvailable)
                    {
                        /* No it is not! - add an entry using the data associated with  */
                        /* the last code and append the first pixel of the last code to */
                        /* make a new entry                                             */
                        phStbGif_addEntry(pContext, pContext->nextAvailable, pContext->lastCode, pContext->pixelLookup[pContext->lastCode].first);
                    }
                    else
                    {
                        /* Yes it is! - add an entry using the data associated with    */
                        /* the last code and append the first pixel of the new code to */
                        /* make a new entry                                            */
                        phStbGif_addEntry(pContext, pContext->nextAvailable, pContext->lastCode, pContext->pixelLookup[pContext->thisCode].first);
                    }
                    /* As the pixels are added to the look up table in reverse order - */
                    /* unwind the sequence of pixels to allow them to be written out.  */
                    sequence = phStbGif_decodeSequence(pContext, pContext->thisCode);
                    for(i=0; i<pContext->pixelLookup[pContext->thisCode].length; i++)
                    {
                        /* Output the pixel associated with this code */
                        phStbGif_outputPixel(pContext, pColourMap, sequence[i]);
                    }
                    /* Increment the number of used data entries */
                    pContext->nextAvailable++;
                    /* Check to see if we need to increase the number of bits in the codes */
                    if (pContext->nextAvailable>=phStbGif_shiftMask[pContext->codeSize+1])
                    {
                       pContext->codeSize++;
                       /* Check to see if we have reached the code size limit */
                       if (pContext->codeSize==PH_STB_GIF_CODE_SIZE_LIMIT)
                       {
                          /* Read the 12 bit clear code */
                          PH_STB_GIF_CHECK(phStbGif_getBitValue(pContext, pContext->codeSize, &(pContext->thisCode)))
                          /* Reset the lookup table to its original values */
                          phStbGif_resetCodeSize(pContext, colourDepth);
                       }
                    }
                }
                /* Save the current code */
                pContext->lastCode=pContext->thisCode;
            }
        }
        else
        {
            /* Run out of data - get out quick */
            return PH_STB_OK;
        }
    }
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readSignature
*       Reads in the file signature information
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readSignature(phStbGif_contextProperties_t* pContext)
{
   int32_t i;
   uint8_t signature[PH_STB_GIF_SIGNATURE_LENGTH];

   for(i=0; i<PH_STB_GIF_SIGNATURE_LENGTH; i++)
   {
      int32_t value;
      value = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      signature[i] = (uint8_t) value;
   }
   if ((memcmp("GIF87a", signature, PH_STB_GIF_SIGNATURE_LENGTH) == 0) ||
       (memcmp("GIF89a", signature, PH_STB_GIF_SIGNATURE_LENGTH) == 0))
   {
       return PH_STB_OK;
   }
   return PH_STB_ERR_FILE_WRONG_FORMAT;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readExtensionBlocks
*       Reads in extension block fields
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readExtensionBlocks(phStbGif_contextProperties_t* pContext)
{
   int32_t header;
   int32_t data[256];
   int32_t functionCode;
   int32_t length;

   while(1)
   {
      int32_t i;

      header = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      /* Check to see if an extension block header byte has been read */
      if (header!=PH_STB_GIF_EXTENSION_HEADER_BYTE)
      {
         phStbGif_putByteValue(pContext, (uint8_t)header);
         return PH_STB_OK;
      }

      /* Read the function code information and associated data */
      functionCode = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      do
      {
          length = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
          for(i=0; i<length; i++)
          {
             data[i] = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
          }

          /* Check to see if the function code corresponds to transparency */
          if (functionCode == PH_STB_GIF_TRANSPARENCY_CODE)
          {
              if (length == 4) 
              {
                  if (((uint32_t)data[0])&PH_STB_GIF_TRANSPARENCY_MASK)
                  {
                      pContext->hasTransparency = true;
                      pContext->transparentColour = data[3];
                  }
                  pContext->duration = (((uint32_t)data[2])<<8)+((uint32_t)data[1]);
                  if (pContext->duration == 0) 
                  {
                      /* Set a default delay */
                      pContext->duration = 10;
                  }
                  pContext->doFillBackground = (bool)((((uint32_t)data[0])&PH_STB_GIF_UNDRAW_MASK)==PH_STB_GIF_RESTORE_BACKGROUND);
              }
          }
      }while(length);
      /* For Comments associated with the image check for code 254 here */
   } 
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readGlobalDescriptor
*       Reads in a global descriptor field
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readGlobalDescriptor(phStbGif_contextProperties_t* pContext)
{
   int32_t value;
   pContext->globalProperties.width = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   pContext->globalProperties.height = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   value = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   pContext->globalProperties.flags = (uint32_t) value;
   pContext->globalProperties.colourDepth = (int32_t)(pContext->globalProperties.flags&PH_STB_GIF_COLOUR_DEPTH_MASK);
   pContext->globalProperties.backgroundColour = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   pContext->globalProperties.aspectRatio = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readLocalDescriptor
*       Reads in a local descriptor field
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readLocalDescriptor(phStbGif_contextProperties_t* pContext)
{
   int32_t value;
   pContext->localProperties.header = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   if (pContext->localProperties.header == PH_STB_GIF_TERMINATOR)
   {
      phStbGif_putByteValue(pContext, (uint8_t)pContext->localProperties.header);
      return PH_STB_OK;
   }
   pContext->localProperties.positionX = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   pContext->localProperties.positionY = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   pContext->localProperties.width = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   pContext->localProperties.height = PH_STB_GIF_GET_BYTE_VALUE(pContext, 2)
   value = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   pContext->localProperties.flags = (uint32_t) value;
   pContext->localProperties.colourDepth = (int32_t)(pContext->localProperties.flags&PH_STB_GIF_COLOUR_DEPTH_MASK);
   pContext->localProperties.interlaced = (int32_t)(pContext->localProperties.flags&PH_STB_GIF_INTERLACED_MASK);
   pContext->localProperties.outputX = 0;
   pContext->localProperties.outputY = 0;
   pContext->localProperties.decodePass = 0;
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readColourMap
*       Reads in a colour map (Palette)
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* numColours    IN      Number of colours in the colour map (palette)
* pColourMap    IN    Pointer to a storage location for the colour map
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readColourMap(phStbGif_contextProperties_t* pContext, uint32_t numColours, uint32_t * pColourMap)
{
   uint32_t i;
   for(i=0; i<numColours; i++)
   {
      int32_t red;
      int32_t green;
      int32_t blue;
      red   = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      green = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      blue  = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)

      pColourMap[i] = (uint32_t)(PH_STB_GIF_FULL_ALPHA|((uint32_t)red<<16)|((uint32_t)green<<8)| (uint32_t)blue);
   }
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_readRasterDataBlock
*       Decodes a block of raster (pixel) data
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* colourDepth   IN      Colour depth of image
* pColourMap    IN      Pointer to colour map (Palette)
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_readRasterDataBlock(phStbGif_contextProperties_t* pContext, int32_t colourDepth, uint32_t const * pColourMap)
{
   int32_t length;
   uint32_t maxEntries;
   /* In the specification it states that black and white images (which have a colour depth of 1) */
   /* should be treated as images with a colour depth of 2 !!!                                    */
   if (colourDepth==1)
   {
       colourDepth=2;
   }
   /* Read in the initial code size */
   pContext->codeSize = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
   if (pContext->codeSize == PH_STB_GIF_TERMINATOR)
   {
      phStbGif_putByteValue(pContext, (uint8_t)pContext->codeSize);
      return PH_STB_OK;
   }
   /* In the specification it states that the code size information should equal the colour depth */
   /* ... so in the case that an invalid code size is read in use the colour depth.               */
   if (pContext->codeSize>=PH_STB_GIF_CODE_SIZE_LIMIT)
   {
       pContext->codeSize = colourDepth;
   }
   pContext->initialCodeSize = pContext->codeSize;
   maxEntries = phStbGif_shiftMask[colourDepth+1];

   /* Calculate the next available data entry */
   pContext->nextAvailable = phStbGif_shiftMask[pContext->codeSize]+2;

   /* Initialise the data entry look up table */
   phStbGif_initLookUp(pContext, maxEntries);

   do
   {
      pContext->currentBit = 0;
      pContext->currentByte = 0;
      pContext->maxBytes = 1;
      /* Decode a block of pixels */
      PH_STB_GIF_CHECK(phStbGif_decodeRasterBlock(pContext, maxEntries, pColourMap))

      /* Read the finishing code */
      length = PH_STB_GIF_GET_BYTE_VALUE(pContext, 1)
      if (length!=0)
      {
         phStbGif_putByteValue(pContext, (uint8_t)length);
      }
   }while(length!=0);

   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_prepareInput
*       Opens the .bmp image file and fill internal data buffers.
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* filename      In      Name of input .gif image file
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_prepareInput(phStbGif_contextProperties_t* pContext, const char* filename)
{
    pContext->pGifInput.file = open(filename, O_RDONLY, 0);

    if (pContext->pGifInput.file < 0) 
    {
        return PH_STB_ERR_FILE_NOT_OPEN;
    }
    pContext->pGifInput.pCurrentByte   = NULL;
    pContext->pGifInput.noOfBytesLeft  = 0;

    return phStbGif_FillBuffer(pContext);
}

/*******************************************************************************
*@begin
* NAME: phStbGif_restartInput
*       Rewind to the start of the image file and fill internal data buffers.
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbGif_restartInput(phStbGif_contextProperties_t* pContext)
{
    (void)lseek(pContext->pGifInput.file, 0L, 0);

    pContext->pGifInput.pCurrentByte   = NULL;
    pContext->pGifInput.noOfBytesLeft  = 0;

    return phStbGif_FillBuffer(pContext);
}

/*******************************************************************************
*@begin
* NAME: phStbGif_closeInput
*       Closes the .gif image file.
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static void phStbGif_closeInput(phStbGif_contextProperties_t const * pContext)
{
    if (pContext->pGifInput.file >= 0) 
    {
        (void)close(pContext->pGifInput.file);
    }
}

/*******************************************************************************
*@begin
* NAME: phStbGif_GetDimensions
*       Gets dimensions of a bitmap supplied in GIF format.
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* filename      IN      Image filename
* pWidth        Out     Width of Image
* pHeight       Out     Height of Image
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
phStbImageErrorCode_t phStbGif_GetDimensions(const char* filename, int32_t * pWidth, int32_t * pHeight )
{
    phStbImageErrorCode_t status;
    phStbGif_contextProperties_t *pContext;

    pContext = (phStbGif_contextProperties_t*)calloc(1, sizeof(phStbGif_contextProperties_t));
    if (pContext == NULL)
    {
        return PH_STB_ERR_OUT_OF_MEMORY;
    }

    PH_STB_GIF_CHECK_LABEL(phStbGif_prepareInput(pContext, filename), clean_up_and_exit_dimensions)
    PH_STB_GIF_CHECK_LABEL(phStbGif_readSignature(pContext), clean_up_and_exit_dimensions)
    PH_STB_GIF_CHECK_LABEL(phStbGif_readGlobalDescriptor(pContext), clean_up_and_exit_dimensions)
    if (pContext->globalProperties.flags&PH_STB_GIF_COLOUR_MAP_MASK)
    {
        PH_STB_GIF_CHECK_LABEL(phStbGif_readColourMap(pContext, phStbGif_shiftMask[pContext->globalProperties.colourDepth+1], pContext->globalColourMap), clean_up_and_exit_dimensions)
    }

    if (pWidth)
    {
        *pWidth = pContext->globalProperties.width;
    }
    if (pHeight)
    {
        *pHeight = pContext->globalProperties.height;
    }

clean_up_and_exit_dimensions:

    phStbGif_closeInput(pContext);
    free(pContext);
    return status;
}

/*******************************************************************************
*@begin
* NAME: phStbGif_timeDiffInMs
*       Returns the difference between 2 time values (in milliseconds).
*       
* Returns : time difference in milliseconds
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* current       IN      Current time value
* start         IN      Initial time value
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static uint32_t phStbGif_timeDiffInMs(struct timeval const *current, struct timeval const *start)
{
    return (uint32_t)((((current->tv_sec - start->tv_sec)*1000000) + (current->tv_usec - start->tv_usec)) / 1000);
}

/*******************************************************************************
*@begin
* NAME: phStbGif_Decode
*       Decodes a bitmap supplied in GIF format.
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* filename      IN      Image filename
* pSetup        IN      Pointer to set up information - holding decode 
*                       information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
phStbImageErrorCode_t phStbGif_Decode( const char* filename, phStbImage_SetupParams_t const * pSetup)
{
    phStbImage_callback callback;
    uint8_t *pInBuffer;
    uint8_t *pInBuffer2;
    int32_t  bufferWidth;
    int32_t  bufferHeight;
    int32_t  outOffsetX;
    int32_t  outOffsetY;
    int32_t  outWidth;
    int32_t  outHeight;
    bool     scale;
    int32_t  moreToProcess;
    int32_t  repeat = 1;
    phStbImageErrorCode_t status;
    phStbGif_contextProperties_t *pContext;
    struct timeval start;
    struct timeval current;

    (void)gettimeofday(&start, NULL);

    /* Check that a valid instance set up structure has been provided */
    if (pSetup == NULL)
    {      
        return PH_STB_ERR_NULL_PARAMETER;
    }

    /* Take local copies of important variables */
    pInBuffer        = pSetup->pInBuffer;
    pInBuffer2       = pSetup->pInBuffer2;
    bufferWidth      = pSetup->InBufferWidth;
    bufferHeight     = pSetup->InBufferHeight;
    scale            = pSetup->bScalingEnabled;
    outOffsetX       = pSetup->OutImageOffsetX;
    outOffsetY       = pSetup->OutImageOffsetY;
    outWidth         = pSetup->OutImageWidth;
    outHeight        = pSetup->OutImageHeight;
    callback         = pSetup->Callback;

    /* Check to see if a valid decode buffer has been provided */
    if (pInBuffer == NULL)
    {
        return PH_STB_ERR_NULL_PARAMETER;
    }

    pContext = (phStbGif_contextProperties_t*)calloc(1, sizeof(phStbGif_contextProperties_t));
    if (pContext == NULL)
    {
        return PH_STB_ERR_OUT_OF_MEMORY;
    }

    PH_STB_GIF_CHECK_LABEL(phStbGif_prepareInput(pContext, filename), clean_up_and_exit_decode)

    while(repeat)
    {
        repeat = 0;
        moreToProcess = 1;

        PH_STB_GIF_CHECK_LABEL(phStbGif_readSignature(pContext), clean_up_and_exit_decode)
        PH_STB_GIF_CHECK_LABEL(phStbGif_readGlobalDescriptor(pContext), clean_up_and_exit_decode)
        if (pContext->globalProperties.flags&PH_STB_GIF_COLOUR_MAP_MASK)
        {
            PH_STB_GIF_CHECK_LABEL(phStbGif_readColourMap(pContext, phStbGif_shiftMask[pContext->globalProperties.colourDepth+1], pContext->globalColourMap), clean_up_and_exit_decode)
        }

        pContext->dataBuffer = (uint32_t*) calloc((uint32_t)(pContext->globalProperties.width*pContext->globalProperties.height), sizeof(uint32_t));
        if (pContext->dataBuffer == NULL)
        {
            DBG_PRINT((dbgGif,DBG_DEFAULT,"phStbGif_Decode: out of memory\n"));
            status = PH_STB_ERR_OUT_OF_MEMORY;
            goto clean_up_and_exit_decode;
        }

        while(moreToProcess)
        {
            int32_t terminator;
            int32_t colourDepth;
            uint32_t * pColourMap;
            pContext->hasTransparency = false;
            pContext->doFillBackground = false;

            PH_STB_GIF_CHECK_AND_STOP(phStbGif_readExtensionBlocks(pContext))
            
            PH_STB_GIF_CHECK_AND_STOP(phStbGif_readLocalDescriptor(pContext))

            if (pContext->localProperties.flags&PH_STB_GIF_COLOUR_MAP_MASK)
            {
                PH_STB_GIF_CHECK_AND_STOP(phStbGif_readColourMap(pContext, phStbGif_shiftMask[pContext->localProperties.colourDepth+1], pContext->localColourMap))
                colourDepth = pContext->localProperties.colourDepth;
                pColourMap = pContext->localColourMap;
            }
            else
            {
                pColourMap = pContext->globalColourMap;
                colourDepth = pContext->globalProperties.colourDepth;
            }

            if (moreToProcess) 
            {
                if (pContext->hasTransparency)
                {
                    pColourMap[pContext->transparentColour] &= PH_STB_GIF_TRANSPARENT_MASK;
                }

                if (pContext->doFillBackground)
                {
                    phStbGif_fillBackground(pContext, pColourMap);
                }

                PH_STB_GIF_CHECK_LABEL(phStbGif_readRasterDataBlock(pContext, colourDepth, pColourMap), clean_up_and_exit_decode)

                terminator = phStbGif_getByteValue(pContext, 1);
                if (terminator == -1)
                {
                    DBG_PRINT((dbgGif,DBG_DEFAULT,"phStbGif_Decode: Error reading terminator\n"));
                    goto clean_up_and_exit_decode;
                }

                moreToProcess = (terminator!=PH_STB_GIF_TERMINATOR);

                if (moreToProcess)
                {
                    phStbGif_putByteValue(pContext, (uint8_t)terminator);
                }
            }

            if (((moreToProcess==0) || callback) &&
                (outOffsetX < bufferWidth) &&
                (outOffsetY < bufferHeight))
            {
                if (scale &&
                    ((pContext->globalProperties.width != outWidth) ||
                     (pContext->globalProperties.height != outHeight)))
                {
                    phStbImage_BitmapScale( pContext->dataBuffer, 
                                            pContext->globalProperties.width, pContext->globalProperties.height,
                                            pInBuffer, pInBuffer2, bufferWidth, bufferHeight, 
                                            outOffsetX, outOffsetY, outWidth, outHeight,
                                            pSetup->gfxMode, pSetup->colourMapFunction);
                }
                else
                {
                    phStbImage_BitmapCopy( pContext->dataBuffer,
                                           pContext->globalProperties.width, pContext->globalProperties.height,
                                           pInBuffer, pInBuffer2, bufferWidth, bufferHeight, 
                                           outOffsetX, outOffsetY, outWidth, outHeight,
                                           pSetup->gfxMode, pSetup->colourMapFunction);
                }
                /* Check to see if there is a required duration for each frame */
                if (callback)
                {
                    (void)gettimeofday(&current, NULL);
                    while(phStbGif_timeDiffInMs(&current, &start) < (pContext->duration * 10))
                    {
                        /* Sleep for 1 ms */
                        (void)usleep(10000);
                        (void)gettimeofday(&current, NULL);
                    }
                    start = current;
                }

                if (callback)
                {
                    if (moreToProcess==0) 
                    {
                        repeat = (callback(phStbImage_eofEvent) == 0);
                        if (repeat) 
                        {
                            PH_STB_GIF_CHECK_LABEL(phStbGif_restartInput(pContext), clean_up_and_exit_decode)

                            /* Free off any memory used during the decoding */
                            if (pContext->dataBuffer) 
                            {
                                free(pContext->dataBuffer);
                            }
                        }
                    }
                    else
                    {
                        if (callback(phStbImage_frameEvent) != 0) 
                        {
                            moreToProcess = 0;
                        }
                    }
                }
            }
        }
    }

clean_up_and_exit_decode :

    phStbGif_closeInput(pContext);

    /* Free off any memory used during the decoding */
    if (pContext->dataBuffer) 
    {
        free(pContext->dataBuffer);
    }
    free(pContext);

    return status;
}


