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

@b  Application:  phStbJpg

*//*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20060329    neill       Original
  2 20060405    neill       Update include file name
  3 20061117    neill       Make library re-entrant
  4 20061117    neill       Changes after crosscheck
  5 20070320    neill       Changes after QAC checking
  6 20071023    neill       More QAC checking
  8 20080208    batelaan    Update file copyright/license text
  9 20080416    neill       Add centre weighted averaging for downscales
 10 20081219    neill       Add support for multiple graphics modes
 11 20090116    neill       Add support for YUV format output
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

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

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

#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h>

#include "phStbImage.h"
#include "phStbImageUtils.h"
#include "phStbJpg.h"

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

#define PH_STB_JPG_FULL_ALPHA               (0xFF)

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

struct my_error_mgr {
  struct jpeg_error_mgr pub;    /* "public" fields */

  jmp_buf setjmp_buffer;    /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

typedef struct
{
   int32_t   width;
   int32_t   height;
   int32_t   scalePosition;
} phStbJpg_GlobalProperties_t;


typedef struct
{
    /* Variables to hold the global and local image properties */
    phStbJpg_GlobalProperties_t globalProperties;

}phStbJpg_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*
********************************************************************************/

static void my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

/*******************************************************************************
*@begin
* NAME: phStbJpg_GetDimensions
*       Gets dimensions of a bitmap supplied in Jpg 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 phStbJpg_GetDimensions( const char* filename, int32_t * pWidth, int32_t * pHeight )
{
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;
    FILE * infile;      /* source file */
    
    infile = fopen(filename, "rb"); 
    if (infile == NULL) 
    {
        (void)fprintf(stderr, "can't open %s\n", filename);
        return PH_STB_ERR_FILE_NOT_OPEN;
    }
    
    /* Step 1: allocate and initialize JPEG decompression object */
    
    /* We set up the normal JPEG error routines, then override error_exit. */
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) 
    {
        /* If we get here, the JPEG code has signaled an error.
         * We need to clean up the JPEG object, close the input file, and return.
         */
        jpeg_destroy_decompress(&cinfo);
        (void)fclose(infile);
        return PH_STB_ERR_FILE_NOT_OPEN;
    }

    /* Now we can initialize the JPEG decompression object. */
    jpeg_create_decompress(&cinfo);
    
    /* Step 2: specify data source (eg, a file) */
    jpeg_stdio_src(&cinfo, infile);
    
    /* Step 3: read file parameters with jpeg_read_header() */    
    (void) jpeg_read_header(&cinfo, TRUE);
    
    if (pWidth)
    {
      *pWidth = (int32_t)cinfo.image_width;
    }
    if (pHeight)
    {
      *pHeight = (int32_t)cinfo.image_height;
    }

    jpeg_destroy_decompress(&cinfo);
    (void)fclose(infile);
    
    return PH_STB_OK;
}

/*******************************************************************************
*@begin
* NAME: phStbJpg_Decode
*       Decodes a bitmap supplied in Jpg 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 phStbJpg_Decode( const char* filename, phStbImage_SetupParams_t const * pSetup)
{
    int32_t *pInBuffer;
    uint8_t * scanline = NULL;
    struct jpeg_decompress_struct cinfo;
    struct my_error_mgr jerr;
    FILE * infile = NULL;       /* source file */
    int32_t row_stride;     /* physical row width in output buffer */
    phStbImageErrorCode_t status = PH_STB_OK;

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

    assert(pSetup->OutImageOffsetX == 0);
    assert(pSetup->OutImageOffsetY == 0);
    assert(!pSetup->bScalingEnabled);
    assert(!pSetup->bFastScalingEnabled);
    
    pInBuffer = (int32_t *)pSetup->pInBuffer;
    
    /* Check to see if a valid decode buffer has been provided */
    if (pInBuffer == NULL)
        return PH_STB_ERR_NULL_PARAMETER;

    infile = fopen(filename, "rb");
    if (infile == NULL) 
    {
        (void)fprintf(stderr, "can't open %s\n", filename);
        status = PH_STB_ERR_FILE_NOT_OPEN;
        goto freeAndReturn;
    }

    /* Step 1: allocate and initialize JPEG decompression object */

    /* We set up the normal JPEG error routines, then override error_exit. */
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(jerr.setjmp_buffer)) 
    {
        /* If we get here, the JPEG code has signaled an error.
         * We need to clean up the JPEG object, close the input file, and return.
         */
        jpeg_destroy_decompress(&cinfo);
        status = PH_STB_ERR_FILE_NOT_OPEN;
        goto freeAndReturn;
    }

    /* Now we can initialize the JPEG decompression object. */
    jpeg_create_decompress(&cinfo);

    /* Step 2: specify data source (eg, a file) */
    jpeg_stdio_src(&cinfo, infile);

    /* Step 3: read file parameters with jpeg_read_header() */    
    (void) jpeg_read_header(&cinfo, TRUE);

    /* Step 5: Start decompressor */
    (void) jpeg_start_decompress(&cinfo);

    row_stride = (int32_t)cinfo.output_width * cinfo.output_components;
    scanline = (uint8_t*)malloc((uint32_t)row_stride);
    if (scanline == NULL)
    {
        DBG_PRINT((dbgJpg,DBG_DEFAULT,"phStbJpg_Decode: out of memory\n"));
        status = PH_STB_ERR_OUT_OF_MEMORY;
        goto freeAndReturn;
    }

    /* Step 6: while (scan lines remain to be read) */
    /*           jpeg_read_scanlines(...); */

    /* Here we use the library's state variable cinfo.output_scanline as the
     * loop counter, so that we don't have to keep track ourselves.
     */
	int32_t output_y = 0;
    if (cinfo.output_components == 1)
    {
        while (cinfo.output_scanline < cinfo.output_height) 
        {
            uint8_t* ptr;
			uint32_t* end_ptr;
            uint32_t* output_ptr;
			uint32_t* end_inbuffer_ptr;
            
            (void) jpeg_read_scanlines(&cinfo, &scanline, 1);

            ptr = scanline;
			end_ptr = ptr + row_stride;
			output_ptr = pInBuffer + (output_y * pSetup->InBufferWidth);
			while(ptr < end_ptr)
			{
			    *output_ptr++ = RGBA8888(*ptr, *ptr, *ptr, PH_STB_JPG_FULL_ALPHA);
                ++ptr;
            }

			end_inbuffer_ptr = pInBuffer + ((output_y + 1) * pSetup->InBufferWidth);
            while(output_ptr < end_inbuffer_ptr)
            {
			    *output_ptr++ = RGBA8888(0, 0, 0, PH_STB_JPG_FULL_ALPHA);
			}
            output_y++;
        }
    }
    else
    {
        while (cinfo.output_scanline < cinfo.output_height) 
        {
            uint8_t* ptr;
			uint32_t* end_ptr;
			uint32_t* output_ptr;
			uint32_t* end_inbuffer_ptr;
            
			(void)jpeg_read_scanlines(&cinfo, &scanline, 1);
            
            ptr = scanline;
			end_ptr = ptr + row_stride;
			output_ptr = pInBuffer + (output_y * pSetup->InBufferWidth);
			while(ptr < end_ptr)
			{
				*output_ptr++ = RGBA8888(*ptr, *(ptr + 1), *(ptr + 2), PH_STB_JPG_FULL_ALPHA);
				ptr += cinfo.output_components;
			}
			
			end_inbuffer_ptr = pInBuffer + ((output_y + 1) * pSetup->InBufferWidth);
            while(output_ptr < end_inbuffer_ptr)
            {
			    *output_ptr++ = RGBA8888(0, 0, 0, PH_STB_JPG_FULL_ALPHA);
			}

			output_y++;
        }
    }

    /* Step 7: Finish decompression */
    (void) jpeg_finish_decompress(&cinfo);

    /* Step 8: Release JPEG decompression object */
    jpeg_destroy_decompress(&cinfo);

freeAndReturn:
    /* Free off any memory used during the decoding */
    if (scanline) 
        free(scanline);

    if (infile)
        (void)fclose(infile);

    return status;
}

