/*****************************************************************************/
/* 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   phStbBmp.c
    @brief  phStbBmp component

@b  Application:  phStbBmp

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20060329    neill       Original
  2 20060405    neill       Performance optimisations
  3 20061009    neill       More performance optimisations
  4 20061010    neill       Fix minor problem when reading first byte of buffer
  5 20061117    neill       Make library re-entrant
  6 20070320    neill       Changes after QAC checking
  7 20071023    neill       More QAC checking
  8 20080208    batelaan    Update file copyright/license text
  9 20081219    neill       Add support for multiple graphics formats
 10 20090116    neill       Add support for YUV format output
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/***********************************************
* INCLUDE FILES                                *
************************************************/

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#include "phStbBmp.h"
#include "phStbImageUtils.h"

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

#define PH_STB_BMP_INPUT_BUFFER_SIZE        (64*1024)

#define PH_STB_BMP_COLOUR_MAP_SIZE          (256)

#define PH_STB_BMP_SIGNATURE                2
#define PH_STB_BMP_FILE_SIZE                4
#define PH_STB_BMP_HEADER_RESERVED          4
#define PH_STB_BMP_DATA_OFFSET              4

#define PH_STB_BMP_IMAGE_INFO_SIZE          4
#define PH_STB_BMP_IMAGE_WIDTH              4
#define PH_STB_BMP_IMAGE_HEIGHT             4
#define PH_STB_BMP_IMAGE_NUM_PLANES         2
#define PH_STB_BMP_IMAGE_BITS_PER_PIXEL     2
#define PH_STB_BMP_IMAGE_COMPRESSION        4
#define PH_STB_BMP_IMAGE_SIZE               4
#define PH_STB_BMP_IMAGE_X_PIXELS_PER_METER 4
#define PH_STB_BMP_IMAGE_Y_PIXELS_PER_METER 4
#define PH_STB_BMP_IMAGE_NUM_COLOURS        4
#define PH_STB_BMP_IMAGE_IMPORTANT_COLOURS  4

#define PH_STB_BMP_FULL_ALPHA               (0xFF)

#define PH_STB_BMP_NO_COMPRESSION           (0)
#define PH_STB_BMP_8_BIT_COMPRESSION        (1)
#define PH_STB_BMP_4_BIT_COMPRESSION        (2)

#define PH_STB_BMP_RED_MASK                 (0x7C00)
#define PH_STB_BMP_RED_SHIFT                (10)
#define PH_STB_BMP_GREEN_MASK               (0x03E0)
#define PH_STB_BMP_GREEN_SHIFT              (5)
#define PH_STB_BMP_BLUE_MASK                (0x001F)
#define PH_STB_BMP_BLUE_SHIFT               (0)

#define catName(pContext, a, b) a##b (pContext)

#define PH_STB_BMP_GET_BYTE_VALUE(pContext, a) \
    (pContext)->getByteVariable = catName(pContext, phStbBmp_getByteValue, a); \
    if ((pContext)->getByteVariable==-1) \
    { \
        return PH_STB_ERR_FILE_TRUNCATED; \
    }

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

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

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

typedef struct _phStbBmp_Input_t
{
    int32_t   file;
    uint8_t   inputBuffer[PH_STB_BMP_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*/
}phStbBmp_Input_t, * pphStbBmp_Input_t;

typedef struct
{
   int32_t width;
   int32_t height;
   int32_t bitsPerPixel;
   int32_t compression;
   int32_t imageSize;
   int32_t numColours;
   int32_t outputX;
   int32_t outputY;
   int32_t scalePosition;
   float scaleWidthFactor;
   float scaleHeightFactor;
   int32_t scaleWidth;
   int32_t scaleHeight;
   int32_t  scaleDown;
} phStbBmp_GlobalProperties_t;


typedef struct
{
    phStbBmp_Input_t pBmpInput;
    
    int32_t getByteVariable;
    
    /* Variables to hold the global and local image properties */
    phStbBmp_GlobalProperties_t globalProperties;
    
    /* Variables used to hold the colour map information */
    uint32_t colourMap[PH_STB_BMP_COLOUR_MAP_SIZE];
    
    /* Pointer to the decode buffer */
    uint32_t * dataBuffer;
    uint32_t * scaleBuffer;
    uint32_t * outputPtr;

} phStbBmp_contextProperties_t;

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/

/*********************************************************(((((((**********
* EXPORTED DATA      g[k|p|kp|pk|kpk]ph[<lnx|tm|NONE>]StbTemplate_<Word>+ *
***************************************************************************/

/*******************************************************************************
* FUNCTION IMPLEMENTATION  <Module>[_<Word>+] for static functions             *
*                          tm[<layer>]<Module>[_<Word>+] for exported functions*
********************************************************************************/

/*******************************************************************************
*@begin
* NAME: phStbBmp_FillBuffer
*       Local function used to refill the input buffert
*       
* 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 phStbBmp_FillBuffer(phStbBmp_contextProperties_t *pContext)
{
    int32_t bytesToRead;
    int32_t bytesRead;

    bytesToRead                   = PH_STB_BMP_INPUT_BUFFER_SIZE;
    pContext->pBmpInput.noOfBytesLeft = 0;
    pContext->pBmpInput.pCurrentByte  = pContext->pBmpInput.inputBuffer;

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

        if (bytesRead < 0) 
        {
            return PH_STB_ERR_FILE_TRUNCATED;
        }

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

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

            return PH_STB_ERR_FILE_TRUNCATED;
        }

        bytesToRead -= bytesRead;

    }while(bytesToRead);

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readFunc
*       Replacement read function used during the BMP decoding
*       
* Returns : PH_STB_OK if successful - else error
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* data          OUT     Pointer to output data
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/

static inline phStbImageErrorCode_t phStbBmp_readFunc(phStbBmp_contextProperties_t *pContext, uint8_t* data)
{
    if (pContext->pBmpInput.noOfBytesLeft == 0)
    {
        if (phStbBmp_FillBuffer(pContext)!=PH_STB_OK)
        {
            /* Error while trying to get more data - give up quick! */
            return PH_STB_ERR_FILE_TRUNCATED;
        }
    }
    *(data++) = *(pContext->pBmpInput.pCurrentByte++);
    pContext->pBmpInput.noOfBytesLeft--;
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_setOutputPtr
*       Set up the output pixel pointer
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbBmp_setOutputPtr(phStbBmp_contextProperties_t *pContext)
{
    if (pContext->globalProperties.scaleDown)
    {
        pContext->outputPtr = pContext->scaleBuffer;
    }
    else
    {
        pContext->outputPtr = pContext->dataBuffer+(pContext->globalProperties.outputY*pContext->globalProperties.width);
    }
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_outputPixel
*       Writes a single pixel value to the output buffer
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* value         IN      Pixel ARGB value
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbBmp_outputPixel(phStbBmp_contextProperties_t *pContext, uint32_t value)
{
    if ((pContext->globalProperties.outputX < pContext->globalProperties.width) &&
        (pContext->globalProperties.outputY < pContext->globalProperties.height) &&
        (pContext->globalProperties.outputY >= 0))
    {
        *(pContext->outputPtr++) = value;
    }
    pContext->globalProperties.outputX++;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_outputColourMapPixel
*       Writes a single colour mapped pixel value to the output buffer
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* which         IN      Index into the colour map
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbBmp_outputColourMapPixel(phStbBmp_contextProperties_t *pContext, int32_t which)
{
    if ((pContext->globalProperties.outputX < pContext->globalProperties.width) &&
        (pContext->globalProperties.outputY < pContext->globalProperties.height) &&
        (pContext->globalProperties.outputY >= 0))
    {
        *(pContext->outputPtr++) = pContext->colourMap[which];
    }
    pContext->globalProperties.outputX++;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_updateScaledInfo
*       Checks to see if a suffiecient number of decoded lines are available
*       to allow the image scaling for the current output line to be done - and
*       then scales the output line of pixels
*       
* Returns : True if scaling was performed else False
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static int32_t phStbBmp_updateScaledInfo(phStbBmp_contextProperties_t *pContext)
{
    int32_t startRow;

    startRow = (int32_t)((float)pContext->globalProperties.scalePosition/pContext->globalProperties.scaleHeightFactor);

    CLAMP_VALUE(startRow, (pContext->globalProperties.height-1))

    /* Check to see if a sufficient number of lines of pixels are available */
    if ((pContext->globalProperties.scalePosition >=0) && (startRow > pContext->globalProperties.outputY))
    {
        int32_t col;
        uint32_t * destPtr;
        uint32_t * srcPtr;

        /* Set up the pointer to the write location in the bitmap */
        destPtr = (uint32_t *)pContext->dataBuffer + (pContext->globalProperties.scaleWidth*pContext->globalProperties.scalePosition);
        srcPtr = pContext->scaleBuffer;

        for(col=0; col<pContext->globalProperties.scaleWidth; col++)
        {
            destPtr[col] = srcPtr[(pContext->globalProperties.width*col)/pContext->globalProperties.scaleWidth];
        }

        /* Move onto the next output line */
        pContext->globalProperties.scalePosition--;
        return 1;
    }
    return 0;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_scaleOutput
*       Checks to see if down-scaling is required - and scales lines as necessary
*       
* Returns : void
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline void phStbBmp_scaleOutput(phStbBmp_contextProperties_t *pContext)
{
    pContext->globalProperties.outputY--;
    pContext->globalProperties.outputX = 0;
    if (pContext->globalProperties.scaleDown)
    {
        while(phStbBmp_updateScaledInfo(pContext))
        {
        }
    }
    phStbBmp_setOutputPtr(pContext);
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_checkEOL
*       Checks to see if the end of a line of pixels has been reached
*       
* Returns : True is end of line reached else False
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline int32_t phStbBmp_checkEOL(phStbBmp_contextProperties_t *pContext)
{
    if (pContext->globalProperties.outputX>=pContext->globalProperties.width)
    {
        phStbBmp_scaleOutput(pContext);
        return 1;
    }
    return 0;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_getByteValue1
*       Gets single byte of data from the input stream
*       
* Returns : Data byte
* 
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline int32_t phStbBmp_getByteValue1(phStbBmp_contextProperties_t *pContext)
{
   uint8_t buffer[1];

   /* Quick check to make most common accesses faster */
   if (pContext->pBmpInput.noOfBytesLeft)
   {
       pContext->pBmpInput.noOfBytesLeft--;
       return (int32_t)*(pContext->pBmpInput.pCurrentByte++);
   }

   if (phStbBmp_readFunc(pContext, buffer)!=PH_STB_OK)
   {
       return -1;
   }
   return (int32_t)buffer[0];
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_getByteValue2
*       Gets 2 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
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline int32_t phStbBmp_getByteValue2(phStbBmp_contextProperties_t *pContext)
{
    int32_t i;
    uint32_t value = 0;

    /* Quick check to make most common accesses faster */
    if (pContext->pBmpInput.noOfBytesLeft>1)
    {
       value = ( (((uint32_t)*(pContext->pBmpInput.pCurrentByte+1))<<8) |
                  ((uint32_t)*(pContext->pBmpInput.pCurrentByte)) );
       
       pContext->pBmpInput.noOfBytesLeft-=2;
       pContext->pBmpInput.pCurrentByte +=2;
       return (int32_t)value;
    }

    for(i=0; i<2; i++)
    {
       int32_t tmpValue;
       tmpValue = phStbBmp_getByteValue1(pContext);
       if (tmpValue == -1)
       {
           return -1;
       }
       value |= (((uint32_t)tmpValue)<<(i*8));
    }
    return (int32_t)value;
}
/*******************************************************************************
*@begin
* NAME: phStbBmp_getByteValue4
*       Gets 4 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
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static inline int32_t phStbBmp_getByteValue4(phStbBmp_contextProperties_t *pContext)
{
    int32_t i;
    uint32_t value = 0;

    /* Quick check to make most common accesses faster */
    if (pContext->pBmpInput.noOfBytesLeft>3)
    {
       value = (((((uint32_t)*(pContext->pBmpInput.pCurrentByte+3))<<24) | 
                 (((uint32_t)*(pContext->pBmpInput.pCurrentByte+2))<<16) | 
                 (((uint32_t)*(pContext->pBmpInput.pCurrentByte+1))<<8) | 
                  ((uint32_t)*(pContext->pBmpInput.pCurrentByte))));
       
       pContext->pBmpInput.noOfBytesLeft-=4;
       pContext->pBmpInput.pCurrentByte +=4;
       return (int32_t)value;
    }

    for(i=0; i<4; i++)
    {
        int32_t tmpValue;
        tmpValue = phStbBmp_getByteValue1(pContext);
        if (tmpValue == -1)
        {
            return -1;
        }
        value |= (((uint32_t)tmpValue)<<(i*8));
    }
    return (int32_t)value;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readHeader
*       Reads in the file header 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 phStbBmp_readHeader(phStbBmp_contextProperties_t *pContext)
{
   int32_t i;
   uint8_t signature[PH_STB_BMP_SIGNATURE];

   /* File signature */
   for(i=0; i<PH_STB_BMP_SIGNATURE; i++)
   {
      int32_t value;
      value = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
      signature[i] = (uint8_t) value;
   }
   if (memcmp("BM", signature, PH_STB_BMP_SIGNATURE) != 0)
   {
       DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP read header: Not a BMP file (%c%c)\n", signature[0], signature[1]));
       return PH_STB_ERR_FILE_WRONG_FORMAT;
   }

   /* File size */
   PH_STB_BMP_GET_BYTE_VALUE(pContext, PH_STB_BMP_FILE_SIZE)
   DBG_PRINT((dbgBmp,DBG_LEVEL_5," FILE_SIZE=%d\n", pContext->getByteVariable ));
   /* Reserved bytes */
   PH_STB_BMP_GET_BYTE_VALUE(pContext, PH_STB_BMP_HEADER_RESERVED)
   DBG_PRINT((dbgBmp,DBG_LEVEL_5," HEADER_RESERVED=%d\n", pContext->getByteVariable ));
   /* Raster data offset */
   PH_STB_BMP_GET_BYTE_VALUE(pContext, PH_STB_BMP_DATA_OFFSET)
   DBG_PRINT((dbgBmp,DBG_LEVEL_5," DATA_OFFSET=%d\n", pContext->getByteVariable ));
   // TODO: shouldn't this offset be used ?  (somehow it works without it  :-o   ) -raiyat
   
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readInfoHeader
*       Reads in the image info header 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 phStbBmp_readImageInfoHeader(phStbBmp_contextProperties_t *pContext)
{
    /* Image info size (=40) */
    PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_INFO_SIZE)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_INFO_SIZE=%d\n", pContext->getByteVariable ));
    /* Image width */
    pContext->globalProperties.width = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_WIDTH)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_WIDTH=%d\n", pContext->getByteVariable ));
    /* Image height */
    pContext->globalProperties.height = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_HEIGHT)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_HEIGHT=%d\n", pContext->getByteVariable ));
    /* Number of image planes (=1) */
    PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_NUM_PLANES)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_NUM_PLANES=%d\n", pContext->getByteVariable ));
    /* Image bits per pixel */
    pContext->globalProperties.bitsPerPixel = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_BITS_PER_PIXEL)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_BITS_PER_PIXEL=%d\n", pContext->getByteVariable ));
    /* Image compression type */
    pContext->globalProperties.compression = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_COMPRESSION)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_COMPRESSION=%d\n", pContext->getByteVariable ));
    /* Image size */
    pContext->globalProperties.imageSize = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_SIZE)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_SIZE=%d\n", pContext->getByteVariable ));
    /* Image pixels per meter (X) */
    PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_X_PIXELS_PER_METER)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_X_PIXELS_PER_METER=%d\n", pContext->getByteVariable ));
    /* Image pixels per meter (Y) */
    PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_Y_PIXELS_PER_METER)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_Y_PIXELS_PER_METER=%d\n", pContext->getByteVariable ));
    /* Image colours used */
    pContext->globalProperties.numColours = PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_NUM_COLOURS)
    if ((!pContext->globalProperties.numColours)
        && (pContext->globalProperties.bitsPerPixel <= 8))
    {
      pContext->globalProperties.numColours = 1 << pContext->globalProperties.bitsPerPixel;
      DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_NUM_COLOURS=0, which in this case means=%d\n", pContext->globalProperties.numColours ));
    }
    else
    {
       DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_NUM_COLOURS=%d\n", pContext->getByteVariable ));
    }
    /* Image 'important' colours */
    PH_STB_BMP_GET_BYTE_VALUE(pContext,PH_STB_BMP_IMAGE_IMPORTANT_COLOURS)
    DBG_PRINT((dbgBmp,DBG_LEVEL_5,"IMAGE_IMPORTANT_COLOURS=%d\n", pContext->getByteVariable ));
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readColourMap
*       
* 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 phStbBmp_readColourMap(phStbBmp_contextProperties_t *pContext)
{
   int32_t i;
   for(i=0; i<pContext->globalProperties.numColours; i++)
   {
      int32_t blue;
      int32_t green;
      int32_t red;
      blue  = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
      green = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
      red   = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
      /* Read the additional 'reserved' value */
      PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)

      pContext->colourMap[i] = RGBA8888(red, green, blue, PH_STB_BMP_FULL_ALPHA);
      DBG_PRINT((dbgBmp,DBG_LEVEL_5,"ColourMap[%d]=0x%X\n", i, pContext->colourMap[i]));
   }
   return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode32BitImage
*       Decodes a 32 bit bmp image
*
* 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 phStbBmp_decode32BitImage(phStbBmp_contextProperties_t *pContext)
{
    int32_t pixelCount;
    int32_t totalPixels = pContext->globalProperties.height*pContext->globalProperties.width;

    for(pixelCount=0; pixelCount<totalPixels; pixelCount++)
    {
        int32_t red;
        int32_t green;
        int32_t blue;
        
        blue  = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        green = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        red   = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)

        phStbBmp_outputPixel(pContext, RGBA8888(red, green, blue, PH_STB_BMP_FULL_ALPHA));

        (void)phStbBmp_checkEOL(pContext);
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode24BitImage
*       Decodes a 24 bit bmp image
*
* 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 phStbBmp_decode24BitImage(phStbBmp_contextProperties_t *pContext)
{
    
    int32_t pixelCount;
    int32_t byteCount = 0;
    int32_t totalPixels = pContext->globalProperties.height*pContext->globalProperties.width;

    for(pixelCount=0; pixelCount<totalPixels; pixelCount++)
    {
        int32_t red;
        int32_t green;
        int32_t blue;
        
        blue  = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        green = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        red   = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        byteCount+=3;

        phStbBmp_outputPixel(pContext, RGBA8888(red, green, blue, PH_STB_BMP_FULL_ALPHA));

        if (phStbBmp_checkEOL(pContext))
        {
            /* Throw away padding bytes */
            while(byteCount%4)
            {
                PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                byteCount++;
            }
        }
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode16BitImage
*       Decodes a 16 bit bmp image
*
* 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 phStbBmp_decode16BitImage(phStbBmp_contextProperties_t *pContext)
{
    int32_t pixelCount;
    int32_t totalPixels = pContext->globalProperties.height*pContext->globalProperties.width;

    for(pixelCount=0; pixelCount<totalPixels; pixelCount++)
    {
        int32_t dataByte;
        uint32_t red;
        uint32_t green;
        uint32_t blue;
        
        dataByte = PH_STB_BMP_GET_BYTE_VALUE(pContext, 2)
        red      = ((uint32_t)dataByte&PH_STB_BMP_RED_MASK)>>PH_STB_BMP_RED_SHIFT;
        green    = ((uint32_t)dataByte&PH_STB_BMP_GREEN_MASK)>>PH_STB_BMP_GREEN_SHIFT;
        blue     = ((uint32_t)dataByte&PH_STB_BMP_BLUE_MASK)>>PH_STB_BMP_BLUE_SHIFT;

        phStbBmp_outputPixel(pContext, RGBA8888(red<<3, green<<3, blue<<3, PH_STB_BMP_FULL_ALPHA));

        (void)phStbBmp_checkEOL(pContext);
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_fillWithZero
*       Fills the image with '0' coloured pixels
*
* Returns : void
*
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* endX      IN  Last X pixel to be filled
* endY      IN  Last Y pixel to be filled
*
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
*
* Additional information:
*
*@end
*******************************************************************************/
static void phStbBmp_fillWithZero(phStbBmp_contextProperties_t *pContext, int32_t endX, int32_t endY)
{
    while((pContext->globalProperties.outputY >= 0) &&
          (endX != pContext->globalProperties.outputX) &&
          (endY != pContext->globalProperties.outputY))
    {
        phStbBmp_outputColourMapPixel(pContext, 0);

        (void)phStbBmp_checkEOL(pContext);
    }
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readUncompressed8BitData
*       Reads 8 bit bmp uncompressed data
*
* Returns : PH_STB_OK if successful else error
*
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* numPixels IN  Number of pixels to read
* checkForEOL   IN  Flag to indicate if the end of line should be checked for
* pByteCount    OUT Pointer to variable to hold the number of bytes read in
*
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
*
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbBmp_readUncompressed8BitData(phStbBmp_contextProperties_t *pContext, int32_t numPixels, int32_t checkForEOL, int32_t *pByteCount)
{
    int32_t pixelCount;
    int32_t dataByte;
    int32_t byteCount = 0;
    for(pixelCount=0; pixelCount<numPixels; pixelCount++)
    {
        dataByte = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        byteCount++;
        
        phStbBmp_outputColourMapPixel(pContext, dataByte);

        if (checkForEOL && phStbBmp_checkEOL(pContext))
        {
            /* Throw away padding bytes */
            while(byteCount%4)
            {
                PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                byteCount++;
            }
        }
    }
    *pByteCount = byteCount;
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode8BitImage
*       Decodes an 8 bit bmp image
*
* 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 phStbBmp_decode8BitImage(phStbBmp_contextProperties_t *pContext)
{
    int32_t byteCount;
    int32_t uncompressedBytes;

    if (pContext->globalProperties.compression == PH_STB_BMP_8_BIT_COMPRESSION)
    {
        int32_t byteIncrement;
        DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: Compressed\n"));
        for(byteCount=0; byteCount<pContext->globalProperties.imageSize; byteCount+=byteIncrement)
        {
            int32_t dataByte[2];
            dataByte[0] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
            dataByte[1] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
            byteIncrement = 2;
            if (dataByte[0]==0)
            {
                switch(dataByte[1])
                {
                    case(0) :
                        phStbBmp_scaleOutput(pContext);
                        break;
                    case(1) :
                        return PH_STB_OK;
                    case(2) :
                        dataByte[0] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                        dataByte[1] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                        phStbBmp_fillWithZero(pContext, 
                                              pContext->globalProperties.outputX+dataByte[0], 
                                              pContext->globalProperties.outputY-dataByte[1]);
                        byteIncrement += 2;
                        break;
                    default :
                        PH_STB_BMP_CHECK(phStbBmp_readUncompressed8BitData(pContext, dataByte[1], 0, &uncompressedBytes))
                        byteIncrement += uncompressedBytes;
                        /* Throw away padding bytes */
                        if (dataByte[1]%2)
                        {
                            PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                            byteIncrement++;
                        }
                        break;
                }
            }
            else
            {
                int32_t i;
                for(i=0; i<dataByte[0]; i++)
                {
                    phStbBmp_outputColourMapPixel(pContext, dataByte[1]);
                }
            }
        }
    }
    else
    {
        DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: Uncompressed\n"));
        PH_STB_BMP_CHECK(phStbBmp_readUncompressed8BitData(pContext, pContext->globalProperties.width*pContext->globalProperties.height, 1, &uncompressedBytes))
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_readUncompressed4BitData
*       Reads 4 bit bmp uncompressed data
*
* Returns : PH_STB_OK if successful else error
*
* Parameter     Flow    Description
* ------------------------------------------------------------------------------
* pContext      IN      Pointer to context information
* numPixels     IN      Number of pixels to read
* checkForEOL   IN      Flag to indicate if the end of line should be checked for
* pByteCount    OUT     Pointer to variable to hold the number of bytes read in
*
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
*
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbBmp_readUncompressed4BitData(phStbBmp_contextProperties_t *pContext, int32_t numPixels, int32_t checkForEOL, int32_t * pByteCount)
{
    int32_t pixelCount;
    int32_t pixelData;
    int32_t dataByte = 0;
    int32_t byteCount = 0;
    for(pixelCount=0; pixelCount<numPixels; pixelCount++)
    {
        if ((pContext->globalProperties.outputX%2)==0)
        {
            dataByte = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
            byteCount++;
            pixelData = (int32_t)(((uint32_t)dataByte)>>4);
        }
        else
        {
            pixelData = (int32_t)(((uint32_t)dataByte)&0x0F);
        }
        
        phStbBmp_outputColourMapPixel(pContext, pixelData);

        if (checkForEOL && phStbBmp_checkEOL(pContext))
        {
            /* Throw away padding bytes */
            while(byteCount%4)
            {
                PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                byteCount++;
            }
        }
    }
    *pByteCount = byteCount;
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode4BitImage
*       Decodes a 4 bit bmp image
*
* 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 phStbBmp_decode4BitImage(phStbBmp_contextProperties_t *pContext)
{
    int32_t byteCount;
    int32_t uncompressedBytes;

    if (pContext->globalProperties.compression == PH_STB_BMP_4_BIT_COMPRESSION)
    {
        int32_t byteIncrement;
        DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: Compressed\n"));
        for(byteCount=0; byteCount<pContext->globalProperties.imageSize; byteCount+=byteIncrement)
        {
            int32_t dataByte[2];
            dataByte[0] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
            dataByte[1] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
            byteIncrement = 2;
            if (dataByte[0]==0)
            {
                switch(dataByte[1])
                {
                    case(0) :
                        phStbBmp_scaleOutput(pContext);
                        break;
                    case(1) :
                        return PH_STB_OK;
                    case(2) :
                        dataByte[0] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                        dataByte[1] = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                        phStbBmp_fillWithZero(pContext,
                                              pContext->globalProperties.outputX+dataByte[0], 
                                              pContext->globalProperties.outputY-dataByte[1]);
                        byteIncrement += 2;
                        break;
                    default :
                        PH_STB_BMP_CHECK(phStbBmp_readUncompressed4BitData(pContext, dataByte[1], 0, &uncompressedBytes))
                        byteIncrement += uncompressedBytes;
                        /* Throw away padding bytes */
                        if (((dataByte[1]%4)==1) || ((dataByte[1]%4)==2))
                        {
                            PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                        }
                        break;
                }
            }
            else
            {
                int32_t i;
                int32_t outputByte[2];
                outputByte[0] = (int32_t)(((uint32_t)dataByte[1])>>4);
                outputByte[1] = (int32_t)(((uint32_t)dataByte[1])&0x0F);
                for(i=0; i<dataByte[0]; i++)
                {
                    phStbBmp_outputColourMapPixel(pContext, outputByte[i%2]);
                }
            }
        }
    }
    else
    {
        DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: Uncompressed\n"));
        PH_STB_BMP_CHECK(phStbBmp_readUncompressed4BitData(pContext, pContext->globalProperties.width*pContext->globalProperties.height, 1, &uncompressedBytes))
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_decode1BitImage
*       Decodes a 1 bit bmp image
*
* 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 phStbBmp_decode1BitImage(phStbBmp_contextProperties_t *pContext)
{
    int32_t byteCount;
    int32_t byteIncrement;

    for(byteCount=0; byteCount<pContext->globalProperties.imageSize; byteCount+=byteIncrement)
    {
        int32_t bit;
        int32_t dataByte = PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
        byteIncrement = 1;

        for(bit=7; bit>=0; bit--)
        {
            phStbBmp_outputColourMapPixel(pContext, (int32_t)((((uint32_t)dataByte)>>bit)&0x01));
        }

        if (phStbBmp_checkEOL(pContext))
        {
            /* Throw away padding bytes */
            while((byteCount+byteIncrement)%4)
            {
                PH_STB_BMP_GET_BYTE_VALUE(pContext, 1)
                byteIncrement++;
            }
        }
    }

    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_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
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbBmp_readRasterDataBlock(phStbBmp_contextProperties_t *pContext)
{
    switch(pContext->globalProperties.bitsPerPixel)
    {
        case(32) :
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 32 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode32BitImage(pContext))
            break;
        case(24) :
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 24 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode24BitImage(pContext))
            break;
        case(16) :
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 16 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode16BitImage(pContext))
            break;
        case(8) :
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 8 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode8BitImage(pContext))
            break;
        case(4) :
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 4 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode4BitImage(pContext))
            break;
        case(1) : 
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: 1 bit format\n"));
            PH_STB_BMP_CHECK(phStbBmp_decode1BitImage(pContext))
            break;
        default : 
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"BMP decode: Unsupported format [%d bits per pixel]\n", pContext->globalProperties.bitsPerPixel));
            return PH_STB_ERR_FILE_WRONG_FORMAT;
    }
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbBmp_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 .bmp image file
* 
* Externals     Flow    Usage
* ------------------------------------------------------------------------------
* 
* Additional information:
*
*@end
*******************************************************************************/
static phStbImageErrorCode_t phStbBmp_prepareInput(phStbBmp_contextProperties_t *pContext, const char* filename)
{
    pContext->pBmpInput.file = open(filename, O_RDONLY, 0);

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

    return phStbBmp_FillBuffer(pContext);
}

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

/*******************************************************************************
*@begin
* NAME: phStbBmp_GetDimensions
*       Gets dimensions of a bitmap supplied in BMP 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 phStbBmp_GetDimensions( const char* filename, int32_t * pWidth, int32_t * pHeight )
{
    phStbImageErrorCode_t status;
    phStbBmp_contextProperties_t *pContext;

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

    PH_STB_BMP_CHECK_LABEL(phStbBmp_prepareInput(pContext, filename), clean_up_and_exit_dimensions)
    PH_STB_BMP_CHECK_LABEL(phStbBmp_readHeader(pContext), clean_up_and_exit_dimensions)
    PH_STB_BMP_CHECK_LABEL(phStbBmp_readImageInfoHeader(pContext), clean_up_and_exit_dimensions)

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

clean_up_and_exit_dimensions:

    phStbBmp_closeInput(pContext);
    free(pContext);

    return status;}

/*******************************************************************************
*@begin
* NAME: phStbBmp_Decode
*       Decodes a bitmap supplied in BMP 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 phStbBmp_Decode( const char* filename, phStbImage_SetupParams_t const * pSetup)
{
    uint8_t *pInBuffer;
    uint8_t *pInBuffer2;
    int32_t  bufferWidth;
    int32_t  bufferHeight;
    int32_t  outOffsetX;
    int32_t  outOffsetY;
    int32_t  outWidth;
    int32_t  outHeight;
    int32_t  imageWidth;
    int32_t  imageHeight;
    phStbImage_GfxMode_t mode;
    phStbImage_cmapFunction cmapFunction; 
    bool     scale;
    phStbImageErrorCode_t status;
    phStbBmp_contextProperties_t *pContext;

    /* 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 */
    scale            = pSetup->bScalingEnabled;
    pInBuffer        = pSetup->pInBuffer;
    pInBuffer2       = pSetup->pInBuffer2;
    bufferWidth      = pSetup->InBufferWidth;
    bufferHeight     = pSetup->InBufferHeight;
    outOffsetX       = pSetup->OutImageOffsetX;
    outOffsetY       = pSetup->OutImageOffsetY;
    outWidth         = pSetup->OutImageWidth;
    outHeight        = pSetup->OutImageHeight;
    mode = pSetup->gfxMode;
    cmapFunction = pSetup->colourMapFunction;

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

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

    PH_STB_BMP_CHECK_LABEL(phStbBmp_prepareInput(pContext, filename), clean_up_and_exit_decode)
    PH_STB_BMP_CHECK_LABEL(phStbBmp_readHeader(pContext), clean_up_and_exit_decode)
    PH_STB_BMP_CHECK_LABEL(phStbBmp_readImageInfoHeader(pContext), clean_up_and_exit_decode)

    if (pContext->globalProperties.bitsPerPixel <= 8)
    {
        PH_STB_BMP_CHECK_LABEL(phStbBmp_readColourMap(pContext), clean_up_and_exit_decode)
    }
    pContext->globalProperties.outputX = 0;
    pContext->globalProperties.outputY = pContext->globalProperties.height-1;

    if (scale &&
        ((pContext->globalProperties.height>outHeight) ||
        (pContext->globalProperties.width>outWidth)))
    {
        float heightScale = (float)outHeight / (float)pContext->globalProperties.height;
        float widthScale = (float)outWidth / (float)pContext->globalProperties.width;

        /* Set up all the variables involved with scaling the image */
        pContext->globalProperties.scaleDown = 1;
        pContext->globalProperties.scaleWidth = imageWidth = outWidth;
        pContext->globalProperties.scaleHeight = imageHeight = outHeight;
        pContext->globalProperties.scalePosition = outHeight-1;
        pContext->globalProperties.scaleWidthFactor = widthScale;
        pContext->globalProperties.scaleHeightFactor = heightScale;

        /* Allocate memory to hold the unscaled line */
        pContext->scaleBuffer = (uint32_t*) calloc((uint32_t)pContext->globalProperties.width, sizeof(uint32_t));
        if (pContext->scaleBuffer==NULL)
        {
            DBG_PRINT((dbgBmp,DBG_DEFAULT,"phStbBmp_Decode: out of memory\n"));
            status = PH_STB_ERR_OUT_OF_MEMORY;
            goto clean_up_and_exit_decode;
        }
    }
    else
    {
        pContext->globalProperties.scaleDown = 0;
        pContext->scaleBuffer = NULL;
        imageWidth = pContext->globalProperties.width;
        imageHeight = pContext->globalProperties.height;
    }

    DBG_PRINT((dbgBmp,DBG_DEFAULT,"phStbBmp_Decode: scaleDown=%d\n", pContext->globalProperties.scaleDown));

    pContext->dataBuffer = (uint32_t*) calloc((uint32_t)(imageWidth*imageHeight), sizeof(uint32_t));
    if (pContext->dataBuffer==NULL)
    {
        DBG_PRINT((dbgBmp,DBG_DEFAULT,"phStbBmp_Decode: out of memory\n"));
        status = PH_STB_ERR_OUT_OF_MEMORY;
        goto clean_up_and_exit_decode;
    }
    
    phStbBmp_setOutputPtr(pContext);

    status = phStbBmp_readRasterDataBlock(pContext);
    if ((status == PH_STB_OK) || (status == PH_STB_ERR_FILE_TRUNCATED))
    {
        if ((outOffsetX < bufferWidth) &&
            (outOffsetY < bufferHeight))
        {
            if (scale &&
                ((imageWidth != outWidth) ||
                 (imageHeight != outHeight)))
            {
                phStbImage_BitmapScale( pContext->dataBuffer, imageWidth, imageHeight,
                                        pInBuffer, pInBuffer2, bufferWidth, bufferHeight, 
                                        outOffsetX, outOffsetY, outWidth, outHeight,
                                        mode, cmapFunction);
            }
            else
            {
                phStbImage_BitmapCopy( pContext->dataBuffer, imageWidth, imageHeight,
                                       pInBuffer, pInBuffer2, bufferWidth, bufferHeight, 
                                       outOffsetX, outOffsetY, outWidth, outHeight,
                                       mode, cmapFunction);
            }
        }
    }


clean_up_and_exit_decode :
    /* Free off any memory used during the decoding */
    phStbBmp_closeInput(pContext);

    if (pContext->dataBuffer) 
    {
        free(pContext->dataBuffer);
    }

    if (pContext->scaleBuffer)
    {
        free(pContext->scaleBuffer);
    }

    free(pContext);

    return status;
}

