/**
  ******************************************************************************
  * @file    jpeg_utils.c 
  * @author  MCD Application Team
  * @version V2.0.0
  * @date    3-June-2016
  * @brief   This driver provides JPEG MCU (Minimum Coded Unit) blocks to RGB and RGB to JPEG MCU conversion functions.
  *
  *    The STM32 HW JPEG decoder/encoder peripheral allows to decode/encode jpeg images.
  *    In decoding, the STM32 HW JPEG data output are organized in blocks called MCU (Minimum Coded Unit)
  *    as specified in the jpeg standard. 
  *    In encoding, the STM32 HW JPEG data input must be organized in MCU blocks.
  *    Depending of the image color space and chrominance sampling, an MCU is generally organized in :
  *    N luminance (Y) blocks + a Blue chrominance (Cb) block + a Red chrominance (Cr) block.
  *    Each block size is 8x8 samples.
  *
  *    The STM32 HW JPEG supports 3 possible color space :
  *      - YCbCr : an MCU is composed of 3 color components : Y , Cb and Cr 
  *      - GrayScale : an MCU is composed of 1 single color component : Y
  *      - CMYK : an MCU is composed of 4 color components : Cyan, Magenta, Yellow, and Key (blacK)
  *                    
  *    This utility file should be used on top of the STM32 HAL JPEG driver. 
  *    In decoding, it allows to convert MCU blocks to RGB888 or ARGB8888 pixels stored to a destination 
  *    frame buffer that can be displayed.
  *    In encoding, it allows to convert RGB888 or ARGB8888 pixels  to MCU blocks.
  *
  *    This utility supports following MCU Chroma sampling format and color space :
  *      - YCbCr 4:2:0 : Each MCU is composed of 4 Y 8x8 blocks + 1 Cb 8x8 block + Cr 8x8 block
  *      - YCbCr 4:2:2 : Each MCU is composed of 2 Y 8x8 blocks + 1 Cb 8x8 block + Cr 8x8 block
  *      - YCbCr 4:4:4 : Each MCU is composed of 1 Y 8x8 block + 1 Cb 8x8 block + Cr 8x8 block
  *      - GrayScale   : Each MCU is composed of 1 Y 8x8 block
  *      - CMYK        : Each MCU is composed of 1 Cyan 8x8  block + 1 Magenta 8x8 block + 1 Yellow 8x8 block + 1 Key 8x8 block.
  *
  *
  * @How to use this driver
  *
  *      - The configuration file "jpeg_utils_conf_template.h" is used to configure this utility
  *        providing some useful flexibilities.
  *        This file should be copied to the application folder and modified as follows:
  *          - Rename it to "jpeg_utils_conf.h".
  *          - Update the name of the JPEG driver's header file, depending on the EVAL board you are using.
  *    
  *        Example if using the STM32F769I-EVAL board :
  *          - Copy the file "jpeg_utils_conf_template.h" to the application folder and rename it to "jpeg_utils_conf.h"
  *          - Edit the "jpeg_utils_conf.h" and change lines 51 and 52 as follow :
  *             #include "stm32f7xx_hal.h"
  *             #include "stm32f7xx_hal_jpeg.h"
  *    
  *        Using this configuration file, user can change the following settings:
  *          - Use RGB888 or ARGB8888 or RGB565 by setting the constant JPEG_RGB_FORMAT respectively to JPEG_RGB888, JPEG_ARGB8888 JPEG_RGB565.
  *          - Swap RED, and Blue offsets if user needs to change the color order to BGR (instead of RGB) by setting:
  *             #define JPEG_SWAP_RB     1
  *          - Enable or disable the decoding post-processing functions (YCbCr to RGB conversion functions) by setting the define USE_JPEG_DECODER 
  *            respectively to 0 or 1.
  *          - Enable or disable the encoding pre-processing functions (RGB to YCbCr conversion functions) by setting the define USE_JPEG_ENCODER 
  *            respectively to 0 or 1.
  *
  *  * For Decoding:
  *      - First, function "JPEG_InitColorTables" should be called to initialize the YCbCr to RGB color
  *        conversion tables. This function needs to be called only one time at the beginning of the 
  *        program whatever the number of jpeg files to be decoded.
  *    
  *      - As soon as the JPEG HW peripheral finished parsing the header of the JPEG input file,
  *        the HAL JPEG callback "HAL_JPEG_InfoReadyCallback" is launched providing the jpeg file
  *        characteristics found in its header.
  *        User can then call the utility function "JPEG_GetDecodeColorConvertFunc" with these
  *        information and retrieve the corresponding color conversion function and number of MCUs.
  *    
  *        Then each time an integer number of MCUs are available (from the HW JPEG output), user 
  *        can call the retrieved function to convert these HW JPEG output data to RGB888 or
  *        ARGB8888 pixel stored to the specified destination buffer.
  *    
  *  * For Encoding:
  *      - First, function "JPEG_InitColorTables" should be called to initialize the YCbCr/RGB color
  *        conversion tables. This function needs to be called only one time at the beginning of the 
  *        program whatever the number of jpeg files to be encoded or decoded.
  *    
  *      - First Use the utility function "JPEG_GetEncodeColorConvertFunc" with the input image informations 
  *        to retrieve the corresponding color conversion function and number of MCUs.
  *    
  *        Then each time an RGB input buffer is available, user can call the retrieved function to convert 
  *        RGB data to MCU blocks stored to the specified destination buffer.
  *
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "jpeg_utils.h"

/** @addtogroup Utilities
  * @{
  */

/** @defgroup JPEG JPEG
* @brief JPEG YCbCr blocks <-> RGB conversion utilties  
* @{
*/

/** @defgroup JPEG_Private_Types JPEG Private Types
* @{
*/ 
/* Private typedef -----------------------------------------------------------*/
typedef struct __JPEG_MCU_RGB_ConvertorTypeDef
{
  uint32_t ColorSpace;
  uint32_t ChromaSubsampling;
  
  uint32_t ImageWidth;
  uint32_t ImageHeight;
  uint32_t ImageSize_Bytes;

  uint32_t LineOffset;
  uint32_t BlockSize;
  
  uint32_t H_factor;
  uint32_t V_factor;

  uint32_t WidthExtend;
  uint32_t ScaledWidth;
  
  uint32_t MCU_Total_Nb;
  
  uint16_t *Y_MCU_LUT;  
  uint16_t *Cb_MCU_LUT;  
  uint16_t *Cr_MCU_LUT;  
  uint16_t *K_MCU_LUT;

}JPEG_MCU_RGB_ConvertorTypeDef;
/**
* @}
*/ 

/** @defgroup JPEG_Private_Defines JPEG Private Defines
* @{
*/ 
/* Private define ------------------------------------------------------------*/
#define YCBCR_420_BLOCK_SIZE       384     /* YCbCr 4:2:0 MCU : 4 8x8 blocks of Y + 1 8x8 block of Cb + 1 8x8 block of Cr   */
#define YCBCR_422_BLOCK_SIZE       256     /* YCbCr 4:2:2 MCU : 2 8x8 blocks of Y + 1 8x8 block of Cb + 1 8x8 block of Cr   */
#define YCBCR_444_BLOCK_SIZE       192     /* YCbCr 4:4:4 MCU : 1 8x8 block of Y + 1 8x8 block of Cb + 1 8x8 block of Cr   */

#define GRAY_444_BLOCK_SIZE        64      /* GrayScale MCU : 1 8x8 block of Y */

#define CMYK_444_BLOCK_SIZE        256     /* CMYK MCU : 1 8x8 blocks of Cyan + 1 8x8 block Magenta + 1 8x8 block of Yellow and 1 8x8 block of BlacK */

#if (JPEG_RGB_FORMAT == JPEG_ARGB8888)
  #define JPEG_GREEN_OFFSET        8       /* Offset of the GREEN color in a pixel         */    
  #define JPEG_ALPHA_OFFSET        24      /* Offset of the Transparency Alpha in a pixel  */
  #define JPEG_BYTES_PER_PIXEL     4       /* Number of bytes in a pixel                   */
  #if (JPEG_SWAP_RB == 0)
    #define JPEG_RED_OFFSET        16      /* Offset of the RED color in a pixel           */
    #define JPEG_BLUE_OFFSET       0       /* Offset of the BLUE color in a pixel          */
  #else
    #define JPEG_RED_OFFSET        0       /* Offset of the RED color in a pixel           */
    #define JPEG_BLUE_OFFSET       16      /* Offset of the BLUE color in a pixel          */
  #endif /* JPEG_SWAP_RB */

#elif(JPEG_RGB_FORMAT == JPEG_RGB888)
  #define JPEG_GREEN_OFFSET        8       /* Offset of the GREEN color in a pixel         */    
  #define JPEG_BYTES_PER_PIXEL     3       /* Number of bytes in a pixel                   */

  #if (JPEG_SWAP_RB == 0)
    #define JPEG_RED_OFFSET        16      /* Offset of the RED color in a pixel           */
    #define JPEG_BLUE_OFFSET       0       /* Offset of the BLUE color in a pixel          */
  #else
   #define JPEG_RED_OFFSET         0       /* Offset of the RED color in a pixel           */
   #define JPEG_BLUE_OFFSET        16      /* Offset of the BLUE color in a pixel          */
  #endif  /* JPEG_SWAP_RB */

#elif (JPEG_RGB_FORMAT == JPEG_RGB565)
  #define JPEG_GREEN_OFFSET        5       /* Offset of the GREEN color in a pixel         */    
  #define JPEG_BYTES_PER_PIXEL     2       /* Number of bytes in a pixel                   */
  #define JPEG_RGB565_GREEN_MASK   0x07E0  /* Mask of Green component in RGB565 Format     */

  #if (JPEG_SWAP_RB == 0)
    #define JPEG_RED_OFFSET        11      /* Offset of the RED color in a pixel           */
    #define JPEG_BLUE_OFFSET       0       /* Offset of the BLUE color in a pixel          */
    #define JPEG_RGB565_RED_MASK   0xF800  /* Mask of Red component in RGB565 Format       */
    #define JPEG_RGB565_BLUE_MASK  0x001F  /* Mask of Blue component in RGB565 Format      */
  #else
    #define JPEG_RED_OFFSET        0       /* Offset of the RED color in a pixel           */
    #define JPEG_BLUE_OFFSET       11      /* Offset of the BLUE color in a pixel          */
    #define JPEG_RGB565_RED_MASK   0x001F  /* Mask of Red component in RGB565 Format       */
    #define JPEG_RGB565_BLUE_MASK  0xF800  /* Mask of Blue component in RGB565 Format      */
  #endif /* JPEG_SWAP_RB */

#else

  #error "unknown JPEG_RGB_FORMAT "

#endif /* JPEG_RGB_FORMAT */

/**
* @}
*/ 

/** @defgroup JPEG_Private_Macros JPEG Private Macros
* @{
*/ 
/* Private macro -------------------------------------------------------------*/
#if (USE_JPEG_DECODER == 1)
#define CLAMP(value) CLAMP_LUT[(value) + 0x100] /* Range limitting macro */
#endif
#if (USE_JPEG_ENCODER == 1)
#define MAX(val1,val2) ((val1 > val2) ? val1 : val2)
#endif
/**
* @}
*/ 

/** @defgroup JPEG_Private_Variables JPEG Private Variables
* @{
*/ 
/* Private variables ---------------------------------------------------------*/

static JPEG_MCU_RGB_ConvertorTypeDef JPEG_ConvertorParams;

#if (USE_JPEG_DECODER == 1)
static int32_t CR_RED_LUT[256];           /* Cr to Red color conversion Look Up Table  */
static int32_t CB_BLUE_LUT[256];          /* Cb to Blue color conversion Look Up Table */
static int32_t CR_GREEN_LUT[256];         /* Cr to Green color conversion Look Up Table*/
static int32_t CB_GREEN_LUT[256];         /* Cb to Green color conversion Look Up Table*/
#endif /* USE_JPEG_DECODER == 1 */

#if (USE_JPEG_ENCODER == 1)
static int32_t RED_Y_LUT[256];            /* Red to Y color conversion Look Up Table  */
static int32_t RED_CB_LUT[256];           /* Red to Cb color conversion Look Up Table  */
static int32_t BLUE_CB_RED_CR_LUT[256];   /* Red to Cr and Blue to Cb color conversion Look Up Table  */
static int32_t GREEN_Y_LUT[256];          /* Green to Y color conversion Look Up Table*/
static int32_t GREEN_CR_LUT[256];         /* Green to Cr color conversion Look Up Table*/
static int32_t GREEN_CB_LUT[256];         /* Green to Cb color conversion Look Up Table*/
static int32_t BLUE_Y_LUT[256];           /* Blue to Y color conversion Look Up Table */
static int32_t BLUE_CR_LUT[256];          /* Blue to Cr color conversion Look Up Table */

/* Different MCU Look Up Table */
static uint16_t JPEG_Y_MCU_LUT[256];
static uint16_t JPEG_Y_MCU_444_LUT[64];

static uint16_t JPEG_Cb_MCU_420_LUT[256];
static uint16_t JPEG_Cb_MCU_422_LUT[256];
static uint16_t JPEG_Cb_MCU_444_LUT[64];

static uint16_t JPEG_Cr_MCU_420_LUT[256];
static uint16_t JPEG_Cr_MCU_422_LUT[256];
static uint16_t JPEG_Cr_MCU_444_LUT[64];

static uint16_t JPEG_K_MCU_420_LUT[256];
static uint16_t JPEG_K_MCU_422_LUT[256];
static uint16_t JPEG_K_MCU_444_LUT[64];

/* YCCK format blocks */
uint8_t kBlocks[16][16];
#endif /* USE_JPEG_ENCODER == 1 */

#if (USE_JPEG_DECODER == 1)
/*  color clamp table : used for range limitting */
static const uint8_t CLAMP_LUT[] = {
/* clamp range 0xffffffff to 0xffffff00 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* clamp range 0x00 to 0xff */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
/* clamp range 0x100 to 0x1ff */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
#endif /* USE_JPEG_DECODER == 1 */
/**
* @}
*/

/** @defgroup JPEG_Private_FunctionPrototypes JPEG Private FunctionPrototypes
* @{
*/ 
/* Private function prototypes -----------------------------------------------*/
#if (USE_JPEG_ENCODER == 1)
static uint32_t JPEG_ARGB_MCU_YCbCr420_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_ARGB_MCU_YCbCr422_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_ARGB_MCU_YCbCr444_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);


static uint32_t JPEG_ARGB_MCU_Gray_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_ARGB_MCU_YCCK_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static void JPEG_Init_MCU_LUT(void);
static void JPEG_InitPreProcColorTables(void);
static uint8_t *JPEG_Set_K_Blocks(uint8_t *pMCUBuffer, uint8_t pKBlocks[16][16], uint32_t ChromaSampling);

#endif /* USE_JPEG_ENCODER == 1 */

#if (USE_JPEG_DECODER == 1)
static uint32_t JPEG_MCU_YCbCr420_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_MCU_YCbCr422_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_MCU_YCbCr444_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);


static uint32_t JPEG_MCU_Gray_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);

static uint32_t JPEG_MCU_YCCK_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount);
static void JPEG_InitPostProcColorTables(void);
#endif /* USE_JPEG_DECODER == 1 */

/**
* @}
*/

/** @defgroup JPEG_Private_Functions JPEG Private Functions
* @{
*/ 

#if (USE_JPEG_ENCODER == 1)
/**
  * @brief  Convert RGB to YCbCr 4:2:0 blocks pixels  
  * @param  pInBuffer  : pointer to input RGB888/ARGB8888 frame buffer.
  * @param  pOutBuffer : pointer to output YCbCr blocks buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from RGB to YCbCr
  */
static uint32_t JPEG_ARGB_MCU_YCbCr420_ConvertBlocks (uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef, colones;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp, offset;
  
  uint32_t red, green, blue;
  
  uint8_t *pOutAddr;
  uint8_t *pInAddr;
  

  numberMCU = ((3 * DataCount) / ( 2 * JPEG_BYTES_PER_PIXEL * YCBCR_420_BLOCK_SIZE));

  currentMCU = BlockIndex;
  *ConvertedDataCount = numberMCU * JPEG_ConvertorParams.BlockSize;

  pOutAddr = &pOutBuffer[0];

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *JPEG_ConvertorParams.H_factor) / JPEG_ConvertorParams.WidthExtend)*JPEG_ConvertorParams.V_factor;
    
    yRef = ((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend);

    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    if(((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend) == 0)
    {
      colones = JPEG_ConvertorParams.H_factor - JPEG_ConvertorParams.LineOffset;
    }
    else
    {
      colones = JPEG_ConvertorParams.H_factor;
    }    
    offset = 0;

    for(i= 0; i <  JPEG_ConvertorParams.V_factor; i+=2)
    {
      
      pInAddr = &pInBuffer[0] ;
      
      for(j=0; j < colones; j+=2)
      {
        /* First Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        cbcomp = (int32_t)(*(RED_CB_LUT + red)) + (int32_t)(*(GREEN_CB_LUT + green)) + (int32_t)(*(BLUE_CB_RED_CR_LUT + blue)) + 128;
        crcomp = (int32_t)(*(BLUE_CB_RED_CR_LUT + red)) + (int32_t)(*(GREEN_CR_LUT + green)) + (int32_t)(*(BLUE_CR_LUT + blue)) + 128;
        
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset]))  = (ycomp);
        (*(pOutAddr + JPEG_ConvertorParams.Cb_MCU_LUT[offset])) = (cbcomp);
        (*(pOutAddr + JPEG_ConvertorParams.Cr_MCU_LUT[offset])) = (crcomp);
        
        /* Second Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + JPEG_BYTES_PER_PIXEL + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + JPEG_BYTES_PER_PIXEL + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + JPEG_BYTES_PER_PIXEL + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_BLUE_OFFSET/8)) ;
#endif  
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset + 1]))  = (ycomp);
        
        /* Third Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + JPEG_ConvertorParams.ScaledWidth + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + JPEG_ConvertorParams.ScaledWidth + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + JPEG_ConvertorParams.ScaledWidth + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset + JPEG_ConvertorParams.H_factor]))  = (ycomp);
        
        /* Fourth Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_ConvertorParams.ScaledWidth + JPEG_BYTES_PER_PIXEL + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset + JPEG_ConvertorParams.H_factor + 1]))  = (ycomp);
        
        /****************/
        
        pInAddr += JPEG_BYTES_PER_PIXEL * 2;
        offset+=2;
      }
      offset += JPEG_ConvertorParams.H_factor + (JPEG_ConvertorParams.H_factor - colones);
      refline += JPEG_ConvertorParams.ScaledWidth * 2 ;
    }
    pOutAddr +=  JPEG_ConvertorParams.BlockSize ;
  }
  return numberMCU;
}

/**
  * @brief  Convert RGB to YCbCr 4:2:2 blocks pixels  
  * @param  pInBuffer  : pointer to input RGB888/ARGB8888 frame buffer.
  * @param  pOutBuffer : pointer to output YCbCr blocks buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from RGB to YCbCr
  */
static uint32_t JPEG_ARGB_MCU_YCbCr422_ConvertBlocks (uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef, colones;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp, offset;
  
  uint32_t red, green, blue;
  
  uint8_t *pOutAddr;
  uint8_t *pInAddr;

  numberMCU = ((2 * DataCount) / (JPEG_BYTES_PER_PIXEL * YCBCR_422_BLOCK_SIZE));

  currentMCU = BlockIndex;
  *ConvertedDataCount = numberMCU * JPEG_ConvertorParams.BlockSize;

  pOutAddr = &pOutBuffer[0];

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *JPEG_ConvertorParams.H_factor) / JPEG_ConvertorParams.WidthExtend)*JPEG_ConvertorParams.V_factor;
    
    yRef = ((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend);

    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    if(((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend) == 0)
    {
      colones = JPEG_ConvertorParams.H_factor - JPEG_ConvertorParams.LineOffset;
    }
    else
    {
      colones = JPEG_ConvertorParams.H_factor;
    }    
    offset = 0;

    for(i= 0; i <  JPEG_ConvertorParams.V_factor; i+=1)
    {
      
      pInAddr = &pInBuffer[0] ;
      
      for(j=0; j < colones; j+=2)
      {
        /* First Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        cbcomp = (int32_t)(*(RED_CB_LUT + red)) + (int32_t)(*(GREEN_CB_LUT + green)) + (int32_t)(*(BLUE_CB_RED_CR_LUT + blue)) + 128;
        crcomp = (int32_t)(*(BLUE_CB_RED_CR_LUT + red)) + (int32_t)(*(GREEN_CR_LUT + green)) + (int32_t)(*(BLUE_CR_LUT + blue)) + 128;
        
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset]))  = ycomp;
        (*(pOutAddr + JPEG_ConvertorParams.Cb_MCU_LUT[offset])) = cbcomp;
        (*(pOutAddr + JPEG_ConvertorParams.Cr_MCU_LUT[offset])) = crcomp;
        
        /* Second Pixel */
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline + JPEG_BYTES_PER_PIXEL)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BYTES_PER_PIXEL + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset + 1]))  = ycomp;
        
        /****************/
        
        pInAddr += JPEG_BYTES_PER_PIXEL * 2;
        offset+=2;
      }
      offset += (JPEG_ConvertorParams.H_factor - colones);
      refline += JPEG_ConvertorParams.ScaledWidth ;
      
    }
    pOutAddr +=  JPEG_ConvertorParams.BlockSize;
  }

  return numberMCU;
}

/**
  * @brief  Convert RGB to YCbCr 4:4:4 blocks pixels  
  * @param  pInBuffer  : pointer to input RGB888/ARGB8888 frame buffer.
  * @param  pOutBuffer : pointer to output YCbCr blocks buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from RGB to YCbCr
  */
static uint32_t JPEG_ARGB_MCU_YCbCr444_ConvertBlocks (uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef, colones;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp, offset;
  
  uint32_t red, green, blue;
  
  uint8_t *pOutAddr;
  uint8_t *pInAddr;

  numberMCU = ((3 * DataCount) / (JPEG_BYTES_PER_PIXEL * YCBCR_444_BLOCK_SIZE));

  currentMCU = BlockIndex;
  *ConvertedDataCount = numberMCU * JPEG_ConvertorParams.BlockSize;

  pOutAddr = &pOutBuffer[0];

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *JPEG_ConvertorParams.H_factor) / JPEG_ConvertorParams.WidthExtend)*JPEG_ConvertorParams.V_factor;
    
    yRef = ((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    if(((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend) == 0)
    {
      colones = JPEG_ConvertorParams.H_factor - JPEG_ConvertorParams.LineOffset;
    }
    else
    {
      colones = JPEG_ConvertorParams.H_factor;
    }    
    offset = 0;

    for(i= 0; i <  JPEG_ConvertorParams.V_factor; i++)
    {
      pInAddr = &pInBuffer[0] ;
      
      for(j=0; j < colones; j++)
      {
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue));
        cbcomp = (int32_t)(*(RED_CB_LUT + red)) + (int32_t)(*(GREEN_CB_LUT + green)) + (int32_t)(*(BLUE_CB_RED_CR_LUT + blue)) + 128;
        crcomp = (int32_t)(*(BLUE_CB_RED_CR_LUT + red)) + (int32_t)(*(GREEN_CR_LUT + green)) + (int32_t)(*(BLUE_CR_LUT + blue)) + 128;
        
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset]))  = (ycomp);
        (*(pOutAddr + JPEG_ConvertorParams.Cb_MCU_LUT[offset])) = (cbcomp);
        (*(pOutAddr + JPEG_ConvertorParams.Cr_MCU_LUT[offset])) = (crcomp);
        
        pInAddr += JPEG_BYTES_PER_PIXEL;
        offset++;
      }
      offset += (JPEG_ConvertorParams.H_factor - colones);
      refline += JPEG_ConvertorParams.ScaledWidth;
    }
    pOutAddr +=  JPEG_ConvertorParams.BlockSize;
  }

  return numberMCU;
}

/**
  * @brief  Convert RGB to Gray blocks pixels  
  * @param  pInBuffer  : pointer to input RGB888/ARGB8888 blocks.
  * @param  pOutBuffer : pointer to output Gray blocks buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from RGB to Gray
  */
static uint32_t JPEG_ARGB_MCU_Gray_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef, colones;

  uint32_t refline;
  int32_t offset;
  
  uint32_t red, green, blue;
  
  uint8_t *pOutAddr;
  uint8_t *pInAddr;
  uint8_t ycomp;

  numberMCU = (DataCount / (JPEG_BYTES_PER_PIXEL * GRAY_444_BLOCK_SIZE));

  currentMCU = BlockIndex;
  *ConvertedDataCount = numberMCU * GRAY_444_BLOCK_SIZE;

  pOutAddr = &pOutBuffer[0];

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *JPEG_ConvertorParams.H_factor) / JPEG_ConvertorParams.WidthExtend)*JPEG_ConvertorParams.V_factor;
    
    yRef = ((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    if(((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend) == 0)
    {
      colones = JPEG_ConvertorParams.H_factor - JPEG_ConvertorParams.LineOffset;
    }
    else
    {
      colones = JPEG_ConvertorParams.H_factor;
    }    
    offset = 0;

    for(i= 0; i <  JPEG_ConvertorParams.V_factor; i++)
    {
      pInAddr = &pInBuffer[0] ;
      
      for(j=0; j < colones; j++)
      {
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + refline + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + refline + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + refline + JPEG_BLUE_OFFSET/8)) ;
#endif
        ycomp  = (uint8_t)((int32_t)(*(RED_Y_LUT + red)) + (int32_t)(*(GREEN_Y_LUT + green)) + (int32_t)(*(BLUE_Y_LUT + blue)));
        
        (*(pOutAddr + offset)) = (ycomp);
        
        pInAddr += JPEG_BYTES_PER_PIXEL;
        offset++;
      }
      offset += (JPEG_ConvertorParams.H_factor - colones);
      refline += JPEG_ConvertorParams.ScaledWidth;
    }
    pOutAddr +=  JPEG_ConvertorParams.BlockSize;
  }

  return numberMCU;
}

/**
  * @brief  Convert RGB to YCCK blocks pixels  
  * @param  pInBuffer  : pointer to input RGB888/ARGB8888 blocks.
  * @param  pOutBuffer : pointer to output YCCK blocks buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from RGB to YCCK
  */
static uint32_t JPEG_ARGB_MCU_YCCK_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef, colones;

  uint32_t refline;
  uint32_t offset;
  
  int32_t red, green,  blue, color_k ;
  
  uint8_t *pOutAddr;
  uint8_t *pInAddr;
  
  numberMCU = ((3 * DataCount) / (JPEG_BYTES_PER_PIXEL * CMYK_444_BLOCK_SIZE));
  *ConvertedDataCount = numberMCU * CMYK_444_BLOCK_SIZE;
  
  currentMCU = BlockIndex;

  pOutAddr = &pOutBuffer[0];

  while(currentMCU < (numberMCU + BlockIndex))
  {        
    xRef = ((currentMCU *JPEG_ConvertorParams.H_factor) / JPEG_ConvertorParams.WidthExtend)*JPEG_ConvertorParams.V_factor;
    
    yRef = ((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend);

    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    if(((currentMCU *JPEG_ConvertorParams.H_factor) % JPEG_ConvertorParams.WidthExtend) == 0)
    {
      colones = JPEG_ConvertorParams.H_factor - JPEG_ConvertorParams.LineOffset;
    }
    else
    {
      colones = JPEG_ConvertorParams.H_factor;
    }    
    offset = 0;
    for(i= 0; i <  JPEG_ConvertorParams.V_factor; i++)
    {  
      pInAddr = &pInBuffer[refline];
      
      for(j=0; j < colones; j++)
      {
#if (JPEG_RGB_FORMAT == JPEG_RGB565)
        red   = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_RED_MASK)   >> JPEG_RED_OFFSET) ;
        green = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_GREEN_MASK) >> JPEG_GREEN_OFFSET) ;
        blue  = (((*(__IO uint16_t *)(pInAddr + refline)) & JPEG_RGB565_BLUE_MASK)  >> JPEG_BLUE_OFFSET) ;
        red   = (red << 3)   | (red >> 2);
        green = (green << 2) | (green >> 4);
        blue  = (blue << 3)  | (blue >> 2);
#else
        red   = (*(pInAddr + JPEG_RED_OFFSET/8)) ;
        green = (*(pInAddr + JPEG_GREEN_OFFSET/8)) ;
        blue  = (*(pInAddr + JPEG_BLUE_OFFSET/8)) ;
#endif
        color_k = MAX(MAX(red,green),blue);
        kBlocks[i][j]=color_k;
        
        (*(pOutAddr + JPEG_ConvertorParams.Y_MCU_LUT[offset]))  = red * 255 / color_k;
        (*(pOutAddr + JPEG_ConvertorParams.Cb_MCU_LUT[offset])) = green * 255 / color_k;
        (*(pOutAddr + JPEG_ConvertorParams.Cr_MCU_LUT[offset])) = blue * 255 / color_k;
        
        pInAddr += JPEG_BYTES_PER_PIXEL;
        offset++;
      }
      offset += (JPEG_ConvertorParams.H_factor - colones);
      refline += JPEG_ConvertorParams.ScaledWidth;
    }
    JPEG_Set_K_Blocks(pOutAddr, kBlocks, JPEG_ConvertorParams.ChromaSubsampling);
    pOutAddr +=  JPEG_ConvertorParams.BlockSize;
  }

  return numberMCU;
}

/**
  * @brief  Retrive Encoding RGB to YCbCr color conversion function and block number  
  * @param  pJpegInfo  : JPEG_ConfTypeDef that contains the JPEG image informations.
  *                      These info are available in the HAL callback "HAL_JPEG_InfoReadyCallback".
  * @param  pFunction  : pointer to JPEG_RGBToYCbCr_Convert_Function , used to retrive the color conversion function 
  *                      depending of the jpeg image color space and chroma sampling info. 
  * @param ImageNbMCUs : pointer to uint32_t, used to retrive the total number of MCU blocks in the jpeg image.  
  * @retval HAL status : HAL_OK or HAL_ERROR.
  */
HAL_StatusTypeDef JPEG_GetEncodeColorConvertFunc(JPEG_ConfTypeDef *pJpegInfo, JPEG_RGBToYCbCr_Convert_Function *pFunction, uint32_t *ImageNbMCUs)
{
  uint32_t hMCU, vMCU;

  JPEG_ConvertorParams.ColorSpace = pJpegInfo->ColorSpace;
  JPEG_ConvertorParams.ChromaSubsampling = pJpegInfo->ChromaSubsampling;
  
  if(JPEG_ConvertorParams.ColorSpace == JPEG_YCBCR_COLORSPACE) 
  {
    if(JPEG_ConvertorParams.ChromaSubsampling == JPEG_420_SUBSAMPLING)
    {
      *pFunction =  JPEG_ARGB_MCU_YCbCr420_ConvertBlocks;  
    }
    else if (JPEG_ConvertorParams.ChromaSubsampling == JPEG_422_SUBSAMPLING)
    {
      *pFunction = JPEG_ARGB_MCU_YCbCr422_ConvertBlocks;  
    }
    else if (JPEG_ConvertorParams.ChromaSubsampling == JPEG_444_SUBSAMPLING)
    {
      *pFunction = JPEG_ARGB_MCU_YCbCr444_ConvertBlocks;  
    }
    else
    {  
       return HAL_ERROR; /* Chroma SubSampling Not supported*/
    }
  }
  else if(JPEG_ConvertorParams.ColorSpace == JPEG_GRAYSCALE_COLORSPACE)
  {
    *pFunction = JPEG_ARGB_MCU_Gray_ConvertBlocks;  
  }
  else if(JPEG_ConvertorParams.ColorSpace == JPEG_CMYK_COLORSPACE)
  {
    *pFunction = JPEG_ARGB_MCU_YCCK_ConvertBlocks;      
  } 
  else
  {  
     return HAL_ERROR; /* Color space Not supported*/
  }

  JPEG_ConvertorParams.ImageWidth = pJpegInfo->ImageWidth;
  JPEG_ConvertorParams.ImageHeight = pJpegInfo->ImageHeight;
  JPEG_ConvertorParams.ImageSize_Bytes = pJpegInfo->ImageWidth * pJpegInfo->ImageHeight * JPEG_BYTES_PER_PIXEL;

  if((JPEG_ConvertorParams.ChromaSubsampling == JPEG_420_SUBSAMPLING) || (JPEG_ConvertorParams.ChromaSubsampling == JPEG_422_SUBSAMPLING))
  {
    JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 16;
   
    JPEG_ConvertorParams.Y_MCU_LUT = JPEG_Y_MCU_LUT;
    
    if(JPEG_ConvertorParams.LineOffset != 0)
    {
      JPEG_ConvertorParams.LineOffset = 16 - JPEG_ConvertorParams.LineOffset;  
    }

    JPEG_ConvertorParams.H_factor = 16;
    
    if(JPEG_ConvertorParams.ChromaSubsampling == JPEG_420_SUBSAMPLING)
    {
      JPEG_ConvertorParams.V_factor  = 16;
      
      if(JPEG_ConvertorParams.ColorSpace == JPEG_YCBCR_COLORSPACE)
      {
        JPEG_ConvertorParams.BlockSize =  YCBCR_420_BLOCK_SIZE;
      }

      JPEG_ConvertorParams.Cb_MCU_LUT = JPEG_Cb_MCU_420_LUT;
      JPEG_ConvertorParams.Cr_MCU_LUT = JPEG_Cr_MCU_420_LUT;

      JPEG_ConvertorParams.K_MCU_LUT  = JPEG_K_MCU_420_LUT;      
    }
    else /* 4:2:2*/
    {
      JPEG_ConvertorParams.V_factor = 8;
      
      if(JPEG_ConvertorParams.ColorSpace == JPEG_YCBCR_COLORSPACE)
      {
        JPEG_ConvertorParams.BlockSize =  YCBCR_422_BLOCK_SIZE;
      }     

      JPEG_ConvertorParams.Cb_MCU_LUT = JPEG_Cb_MCU_422_LUT;
      JPEG_ConvertorParams.Cr_MCU_LUT = JPEG_Cr_MCU_422_LUT;
      
      JPEG_ConvertorParams.K_MCU_LUT  = JPEG_K_MCU_422_LUT;        
    }    
  }
  else if(JPEG_ConvertorParams.ChromaSubsampling == JPEG_444_SUBSAMPLING)
  {
    JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 8;
    
    JPEG_ConvertorParams.Y_MCU_LUT = JPEG_Y_MCU_444_LUT;

    JPEG_ConvertorParams.Cb_MCU_LUT = JPEG_Cb_MCU_444_LUT;
    JPEG_ConvertorParams.Cr_MCU_LUT = JPEG_Cr_MCU_444_LUT;
    
    JPEG_ConvertorParams.K_MCU_LUT  = JPEG_K_MCU_444_LUT;    
        
    if(JPEG_ConvertorParams.LineOffset != 0)
    {
      JPEG_ConvertorParams.LineOffset = 8 - JPEG_ConvertorParams.LineOffset;  
    }
    JPEG_ConvertorParams.H_factor = 8;
    JPEG_ConvertorParams.V_factor = 8;
 
    if(JPEG_ConvertorParams.ColorSpace == JPEG_YCBCR_COLORSPACE)
    {
      JPEG_ConvertorParams.BlockSize = YCBCR_444_BLOCK_SIZE;
    }
    if(JPEG_ConvertorParams.ColorSpace == JPEG_CMYK_COLORSPACE)
    {
      JPEG_ConvertorParams.BlockSize = CMYK_444_BLOCK_SIZE;
    }    
    else if(JPEG_ConvertorParams.ColorSpace == JPEG_GRAYSCALE_COLORSPACE)
    {
      JPEG_ConvertorParams.BlockSize = GRAY_444_BLOCK_SIZE;
    }
   
  }
  else
  {
     return HAL_ERROR; /* Not supported*/
  }
  

  JPEG_ConvertorParams.WidthExtend = JPEG_ConvertorParams.ImageWidth + JPEG_ConvertorParams.LineOffset;
  JPEG_ConvertorParams.ScaledWidth = JPEG_BYTES_PER_PIXEL * JPEG_ConvertorParams.ImageWidth; 
  
  hMCU = (JPEG_ConvertorParams.ImageWidth / JPEG_ConvertorParams.H_factor);
  if((JPEG_ConvertorParams.ImageWidth % JPEG_ConvertorParams.H_factor) != 0)
  {
    hMCU++; /*+1 for horizenatl incomplete MCU */                
  }

  vMCU = (JPEG_ConvertorParams.ImageHeight / JPEG_ConvertorParams.V_factor);
  if((JPEG_ConvertorParams.ImageHeight % JPEG_ConvertorParams.V_factor) != 0)
  {
    vMCU++; /*+1 for vertical incomplete MCU */                
  }
  JPEG_ConvertorParams.MCU_Total_Nb = (hMCU * vMCU);
  *ImageNbMCUs = JPEG_ConvertorParams.MCU_Total_Nb;
  
  return HAL_OK;
}
#endif /* USE_JPEG_ENCODER == 1 */

#if (USE_JPEG_DECODER == 1)
/**
  * @brief  Convert YCbCr 4:2:0 blocks to RGB pixels  
  * @param  pInBuffer  : pointer to input YCbCr blocks buffer.
  * @param  pOutBuffer : pointer to output RGB888/ARGB8888 frame buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from YCbCr to RGB 
  */

static uint32_t JPEG_MCU_YCbCr420_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j,k, currentMCU, xRef,yRef;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp;

  int32_t c_red, c_blue, c_green;
  
  uint8_t *pOutAddr, *pOutAddr2;
  uint8_t *pChrom, *pLum;
  
  numberMCU = DataCount / YCBCR_420_BLOCK_SIZE;
  currentMCU = BlockIndex;
  

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *16) / JPEG_ConvertorParams.WidthExtend)*16;
    
    yRef = ((currentMCU *16) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    pChrom = pInBuffer + 256; /* pChroma = pInBuffer + 4*64 */
    
    pLum = pInBuffer;
    
    for(i= 0; i <  16; i+=2)
    {
      if(i == 8)
      {
        pLum = pInBuffer + 128;
      }
      
      if(refline < JPEG_ConvertorParams.ImageSize_Bytes)
      {
        pOutAddr = pOutBuffer + refline;
        pOutAddr2 = pOutAddr + JPEG_ConvertorParams.ScaledWidth;
        
        for(k= 0; k<2; k++)
        {
          for(j=0; j < 8; j+=2)
          {           
            cbcomp = (int32_t)(*(pChrom));
            c_blue = (int32_t)(*(CB_BLUE_LUT + cbcomp));
            
            crcomp = (int32_t)(*(pChrom + 64));
            c_red = (int32_t)(*(CR_RED_LUT + crcomp));          
            
            c_green = ((int32_t)(*(CR_GREEN_LUT + crcomp)) + (int32_t)(*(CB_GREEN_LUT + cbcomp))) >> 16;      
          
            
            
#if (JPEG_RGB_FORMAT == JPEG_ARGB8888)
          
            ycomp = (int32_t)(*(pLum +j));
            
            *(__IO uint32_t *)pOutAddr = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));
            
            *((__IO uint32_t *)(pOutAddr + 4)) = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +8));
            
            *(__IO uint32_t *)pOutAddr2 = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +8 +1));
            
            *((__IO uint32_t *)(pOutAddr2 +4)) = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);

            
#elif (JPEG_RGB_FORMAT == JPEG_RGB888)
          
           ycomp = (int32_t)(*(pLum +j));
          
            pOutAddr[JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr[JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr[JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));

            pOutAddr[3 + JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr[3 + JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr[3 + JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);

            /**********/            
            ycomp = (int32_t)(*(pLum +j +8));

            pOutAddr2[JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr2[JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr2[JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +8 +1));            
            
            pOutAddr2[3+ JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr2[3 + JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr2[3 + JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);
 
#elif (JPEG_RGB_FORMAT == JPEG_RGB565)
          
            ycomp = (int32_t)(*(pLum +j));
            
            *(__IO uint16_t *)pOutAddr = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));
            
            *((__IO uint16_t *)(pOutAddr + 2)) = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +8));
            
            *(__IO uint16_t *)pOutAddr2 = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +8 +1));
            
            *((__IO uint16_t *)(pOutAddr2 +2)) = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);         
#endif /* JPEG_RGB_FORMAT */          
          
            pOutAddr += JPEG_BYTES_PER_PIXEL * 2;
            pOutAddr2 += JPEG_BYTES_PER_PIXEL * 2;
          
            pChrom++;
          }
          pLum += 64;                      
        }

        pLum = pLum - 128 + 16;
        
        refline += 2*JPEG_ConvertorParams.ScaledWidth;          
      }
    }   
    
    pInBuffer +=  YCBCR_420_BLOCK_SIZE;
  }
  return numberMCU;
}

/**
  * @brief  Convert YCbCr 4:2:2 blocks to RGB pixels  
  * @param  pInBuffer  : pointer to input YCbCr blocks buffer.
  * @param  pOutBuffer : pointer to output RGB888/ARGB8888 frame buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from YCbCr to RGB 
  */
static uint32_t JPEG_MCU_YCbCr422_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer,
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j,k, currentMCU, xRef,yRef;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp;

  int32_t c_red, c_blue, c_green;
  
  uint8_t *pOutAddr;
  uint8_t *pChrom, *pLum;
  
  numberMCU = DataCount / YCBCR_422_BLOCK_SIZE;
  currentMCU = BlockIndex;
  

  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *16) / JPEG_ConvertorParams.WidthExtend)*8;
    
    yRef = ((currentMCU *16) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    pChrom = pInBuffer + 128; /* pChroma = pInBuffer + 2*64 */
    
    pLum = pInBuffer;
    
    for(i= 0; i <  8; i++)
    {
       if(refline < JPEG_ConvertorParams.ImageSize_Bytes)
      {
        pOutAddr = pOutBuffer + refline;
        
        for(k= 0; k<2; k++)
        {
          for(j=0; j < 8; j+=2)
          {           
            cbcomp = (int32_t)(*(pChrom));
            c_blue = (int32_t)(*(CB_BLUE_LUT + cbcomp));
            
            crcomp = (int32_t)(*(pChrom + 64));
            c_red = (int32_t)(*(CR_RED_LUT + crcomp));          
            
            c_green = ((int32_t)(*(CR_GREEN_LUT + crcomp)) + (int32_t)(*(CB_GREEN_LUT + cbcomp))) >> 16;      
          
            
            
#if (JPEG_RGB_FORMAT == JPEG_ARGB8888) 
          
            ycomp = (int32_t)(*(pLum +j));
            
            *(__IO uint32_t *)pOutAddr = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));
            
            *((__IO uint32_t *)(pOutAddr + 4)) = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);         
            
#elif (JPEG_RGB_FORMAT == JPEG_RGB888)
          
           ycomp = (int32_t)(*(pLum +j));
          
            pOutAddr[JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr[JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr[JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);
            
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));

            pOutAddr[3 + JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr[3 + JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr[3 + JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);

#elif (JPEG_RGB_FORMAT == JPEG_RGB565)  
          
            ycomp = (int32_t)(*(pLum +j));
            
            *(__IO uint16_t *)pOutAddr = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);
            /**********/
            ycomp = (int32_t)(*(pLum +j +1));
            
            *((__IO uint16_t *)(pOutAddr + 2)) = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);         
          
#endif /* JPEG_RGB_FORMAT*/          
          
            pOutAddr += JPEG_BYTES_PER_PIXEL * 2;
          
            pChrom++;
          }
          pLum += 64;                      
        }
        
        pLum = pLum - 128 + 8;
        
        refline += JPEG_ConvertorParams.ScaledWidth;          
      }
    }   
    
    pInBuffer +=  YCBCR_422_BLOCK_SIZE;
  }
  return numberMCU;
}

/**
  * @brief  Convert YCbCr 4:4:4 blocks to RGB pixels  
  * @param  pInBuffer  : pointer to input YCbCr blocks buffer.
  * @param  pOutBuffer : pointer to output RGB888/ARGB8888 frame buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from YCbCr to RGB 
  */
static uint32_t JPEG_MCU_YCbCr444_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer,
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef;

  uint32_t refline;
  int32_t ycomp, crcomp, cbcomp;
  
  int32_t c_red, c_blue, c_green;
  
  uint8_t *pOutAddr;
  uint8_t *pChrom, *pLum;
  
  numberMCU = DataCount / YCBCR_444_BLOCK_SIZE;
  currentMCU = BlockIndex;
  

  while(currentMCU < (numberMCU + BlockIndex))
  {

    xRef = ((currentMCU *8) / JPEG_ConvertorParams.WidthExtend)*8;
    
    yRef = ((currentMCU *8) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;   
    
    pChrom = pInBuffer + 64; /* pChroma = pInBuffer + 4*64 */
    
    pLum = pInBuffer;
    
    for(i= 0; i <  8; i++)
    {
      if(refline < JPEG_ConvertorParams.ImageSize_Bytes)
      {
        pOutAddr = pOutBuffer+ refline;
        

          for(j=0; j < 8; j++)
          {           
            cbcomp = (int32_t)(*pChrom);
            c_blue = (int32_t)(*(CB_BLUE_LUT + cbcomp));
            
            crcomp = (int32_t)(*(pChrom + 64));
            c_red = (int32_t)(*(CR_RED_LUT + crcomp));          
            
            c_green = ((int32_t)(*(CR_GREEN_LUT + crcomp)) + (int32_t)(*(CB_GREEN_LUT + cbcomp))) >> 16;      
                      
#if (JPEG_RGB_FORMAT == JPEG_ARGB8888)
          
            ycomp = (int32_t)(*(pLum +j));
            
            *(__IO uint32_t *)pOutAddr = 
              (CLAMP(ycomp + c_red) << JPEG_RED_OFFSET)     | \
              (CLAMP( ycomp + c_green) << JPEG_GREEN_OFFSET) | \
              (CLAMP(ycomp + c_blue) << JPEG_BLUE_OFFSET);       
            
#elif (JPEG_RGB_FORMAT == JPEG_RGB888)
            
            ycomp = (int32_t)(*(pLum +j));
          
            pOutAddr[JPEG_RED_OFFSET/8] = CLAMP(ycomp + c_red);
            pOutAddr[JPEG_GREEN_OFFSET/8] = CLAMP(ycomp + c_green);
            pOutAddr[JPEG_BLUE_OFFSET/8] = CLAMP(ycomp + c_blue);        

#elif (JPEG_RGB_FORMAT == JPEG_RGB565)  
            
            ycomp = (int32_t)(*(pLum +j));
          
            *(__IO uint16_t *)pOutAddr = 
              ((CLAMP(ycomp + c_red) >> 3) << JPEG_RED_OFFSET)     | \
              ((CLAMP( ycomp + c_green) >> 2) << JPEG_GREEN_OFFSET) | \
              ((CLAMP(ycomp + c_blue) >> 3) << JPEG_BLUE_OFFSET);      
            
#endif /* JPEG_RGB_FORMAT */          
          
            pOutAddr += JPEG_BYTES_PER_PIXEL;
          
            pChrom++;
          }
          pLum += 8;

        refline += JPEG_ConvertorParams.ScaledWidth;          
      }
    }   
    
    pInBuffer +=  YCBCR_444_BLOCK_SIZE;
  }
  return numberMCU;
}

/**
  * @brief  Convert Y Gray blocks to RGB pixels  
  * @param  pInBuffer  : pointer to input Luminance Y blocks buffer.
  * @param  pOutBuffer : pointer to output RGB888/ARGB8888 frame buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from YCbCr to RGB 
  */
static uint32_t JPEG_MCU_Gray_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer,
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{
  uint32_t numberMCU;
  uint32_t  currentMCU, xRef,yRef;
  uint32_t refline;
  

  uint32_t i,j, ySample;
  uint8_t *pOutAddr,  *pLum;

  
  numberMCU = DataCount / GRAY_444_BLOCK_SIZE;
  currentMCU = BlockIndex;
  
  while(currentMCU < (numberMCU + BlockIndex))
  {
    xRef = ((currentMCU *8) / JPEG_ConvertorParams.WidthExtend)*8;
    
    yRef = ((currentMCU *8) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);
    
    currentMCU++;
  
    pLum = pInBuffer;
    
    for(i= 0; i <  8; i++)
    { 
      pOutAddr = pOutBuffer + refline;
      if(refline < JPEG_ConvertorParams.ImageSize_Bytes)
      {  
        for(j=0; j < 8; j++)
        { 
          ySample =   (uint32_t)(*pLum);

#if (JPEG_RGB_FORMAT == JPEG_ARGB8888)
          
          *(__IO uint32_t *)pOutAddr = ySample |  (ySample << 8) | (ySample << 16);
          
#elif (JPEG_RGB_FORMAT == JPEG_RGB888)
          
          pOutAddr[0] =  ySample;
          pOutAddr[1] =  ySample;
          pOutAddr[2] =  ySample;     
 
#elif (JPEG_RGB_FORMAT == JPEG_RGB565)
            
          *(__IO uint16_t *)pOutAddr = ((ySample >> 3) << 11) |  ((ySample >> 2) << 5) | (ySample >> 3);     
          
#endif /* JPEG_RGB_FORMAT */          
          
          pOutAddr += JPEG_BYTES_PER_PIXEL;
          pLum++;
        }

        refline += JPEG_ConvertorParams.ScaledWidth;        
      }
    }
    
    pInBuffer +=  GRAY_444_BLOCK_SIZE;    
  }
  return numberMCU;
}

/**
  * @brief  Convert CMYK blocks to RGB pixels  
  * @param  pInBuffer  : pointer to input CMYK blocks buffer.
  * @param  pOutBuffer : pointer to output RGB888/ARGB8888 frame buffer.
  * @param  BlockIndex : index of the input buffer first block in the final image.
  * @param  DataCount  : number of bytes in the input buffer .
  * @param  ConvertedDataCount  : number of converted bytes from input buffer.  
  * @retval Number of blcoks converted from CMYK to RGB 
  */
static uint32_t JPEG_MCU_YCCK_ARGB_ConvertBlocks(uint8_t *pInBuffer, 
                                      uint8_t *pOutBuffer, 
                                      uint32_t BlockIndex,
                                      uint32_t DataCount,
                                      uint32_t *ConvertedDataCount)
{  
  uint32_t numberMCU;
  uint32_t i,j, currentMCU, xRef,yRef;

  uint32_t refline;
  int32_t color_k;
  
  int32_t c_red, c_blue, c_green;
  
  uint8_t *pOutAddr, *pChrom;
  
  numberMCU = DataCount / CMYK_444_BLOCK_SIZE;
  currentMCU = BlockIndex;
  

  while(currentMCU < (numberMCU + BlockIndex))
  {

    xRef = ((currentMCU *8) / JPEG_ConvertorParams.WidthExtend)*8;
    
    yRef = ((currentMCU *8) % JPEG_ConvertorParams.WidthExtend);
    
    refline = JPEG_ConvertorParams.ScaledWidth * xRef + (JPEG_BYTES_PER_PIXEL*yRef);

    currentMCU++;
    
    pChrom = pInBuffer;
    
    for(i= 0; i <  8; i++)
    {
      if(refline < JPEG_ConvertorParams.ImageSize_Bytes)
      {
        pOutAddr = pOutBuffer+ refline;        

          for(j=0; j < 8; j++)
          {           
            color_k = (int32_t)(*(pChrom + 192));
            c_red = (color_k * ((int32_t)(*pChrom)))/255;
            
            c_green = (color_k * (int32_t)(*(pChrom + 64)))/255;
            
            c_blue = (color_k * (int32_t)(*(pChrom + 128)))/255;
                      
#if (JPEG_RGB_FORMAT == JPEG_ARGB8888)
          
          *(__IO uint32_t *)pOutAddr = 
            (c_red << JPEG_RED_OFFSET) | \
            (c_green << JPEG_GREEN_OFFSET) | \
            (c_blue << JPEG_BLUE_OFFSET);     
            
#elif (JPEG_RGB_FORMAT == JPEG_RGB888)
            
          pOutAddr[JPEG_RED_OFFSET/8]   =  c_red;
          pOutAddr[JPEG_GREEN_OFFSET/8] =  c_green;
          pOutAddr[JPEG_BLUE_OFFSET/8]  =  c_blue;
          
#elif (JPEG_RGB_FORMAT == JPEG_RGB565)
            
          *(__IO uint16_t *)pOutAddr = 
            ((c_red >> 3) << JPEG_RED_OFFSET)     | \
            ((c_green >> 2) << JPEG_GREEN_OFFSET) | \
            ((c_blue >> 3) << JPEG_BLUE_OFFSET);         
            
#endif /* JPEG_RGB_FORMAT */          
          
            pOutAddr += JPEG_BYTES_PER_PIXEL;
          
            pChrom++;
          }

        refline += JPEG_ConvertorParams.ScaledWidth;          
      }
    }   
    
    pInBuffer +=  CMYK_444_BLOCK_SIZE;
  }
  return numberMCU;
}

/**
  * @brief  Retrive Decoding YCbCr to RGB color conversion function and block number  
  * @param  pJpegInfo  : JPEG_ConfTypeDef that contains the JPEG image informations.
  *                      These info are available in the HAL callback "HAL_JPEG_InfoReadyCallback".
  * @param  pFunction  : pointer to JPEG_YCbCrToRGB_Convert_Function , used to retrive the color conversion function 
  *                      depending of the jpeg image color space and chroma sampling info. 
  * @param ImageNbMCUs : pointer to uint32_t, used to retrive the total number of MCU blocks in the jpeg image.  
  * @retval HAL status : HAL_OK or HAL_ERROR.
  */
HAL_StatusTypeDef JPEG_GetDecodeColorConvertFunc(JPEG_ConfTypeDef *pJpegInfo, JPEG_YCbCrToRGB_Convert_Function *pFunction, uint32_t *ImageNbMCUs)
{
  uint32_t hMCU, vMCU;

  JPEG_ConvertorParams.ColorSpace = pJpegInfo->ColorSpace;
  JPEG_ConvertorParams.ImageWidth = pJpegInfo->ImageWidth;
  JPEG_ConvertorParams.ImageHeight = pJpegInfo->ImageHeight;
  JPEG_ConvertorParams.ImageSize_Bytes = pJpegInfo->ImageWidth * pJpegInfo->ImageHeight * JPEG_BYTES_PER_PIXEL;
  
  JPEG_ConvertorParams.ChromaSubsampling = pJpegInfo->ChromaSubsampling;  
  if(JPEG_ConvertorParams.ColorSpace == JPEG_YCBCR_COLORSPACE)
  {
    if(JPEG_ConvertorParams.ChromaSubsampling == JPEG_420_SUBSAMPLING)
    {
      *pFunction = JPEG_MCU_YCbCr420_ARGB_ConvertBlocks;

      JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 16;
      
      if(JPEG_ConvertorParams.LineOffset != 0)
      {
        JPEG_ConvertorParams.LineOffset = 16 - JPEG_ConvertorParams.LineOffset;  
      }
      JPEG_ConvertorParams.H_factor = 16;
      JPEG_ConvertorParams.V_factor  = 16;
    }
    else if(JPEG_ConvertorParams.ChromaSubsampling == JPEG_422_SUBSAMPLING)
    {
      *pFunction =  JPEG_MCU_YCbCr422_ARGB_ConvertBlocks;

      JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 16;
      
      if(JPEG_ConvertorParams.LineOffset != 0)
      {
        JPEG_ConvertorParams.LineOffset = 16 - JPEG_ConvertorParams.LineOffset;  
      }
      JPEG_ConvertorParams.H_factor = 16;
      JPEG_ConvertorParams.V_factor  = 8;
    }
    else /*4:4:4*/
    {
      *pFunction =  JPEG_MCU_YCbCr444_ARGB_ConvertBlocks;

      JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 8;
      
      if(JPEG_ConvertorParams.LineOffset != 0)
      {
        JPEG_ConvertorParams.LineOffset = 8 - JPEG_ConvertorParams.LineOffset;  
      }
      JPEG_ConvertorParams.H_factor = 8;
      JPEG_ConvertorParams.V_factor  = 8;
    }   
  }
  else if(JPEG_ConvertorParams.ColorSpace == JPEG_GRAYSCALE_COLORSPACE)
  {
    *pFunction =  JPEG_MCU_Gray_ARGB_ConvertBlocks;

    JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 8;
      
    if(JPEG_ConvertorParams.LineOffset != 0)
    {
      JPEG_ConvertorParams.LineOffset = 8 - JPEG_ConvertorParams.LineOffset;  
    }
    JPEG_ConvertorParams.H_factor = 8;
    JPEG_ConvertorParams.V_factor  = 8;
  }
  else if(JPEG_ConvertorParams.ColorSpace == JPEG_CMYK_COLORSPACE)
  {
    *pFunction =  JPEG_MCU_YCCK_ARGB_ConvertBlocks;

    JPEG_ConvertorParams.LineOffset = JPEG_ConvertorParams.ImageWidth % 8;
      
    if(JPEG_ConvertorParams.LineOffset != 0)
    {
      JPEG_ConvertorParams.LineOffset = 8 - JPEG_ConvertorParams.LineOffset;  
    }
    JPEG_ConvertorParams.H_factor = 8;
    JPEG_ConvertorParams.V_factor  = 8;
  } 
  else
  {  
    return HAL_ERROR; /* Color space Not supported*/
  }
 
  JPEG_ConvertorParams.WidthExtend = JPEG_ConvertorParams.ImageWidth + JPEG_ConvertorParams.LineOffset;
  JPEG_ConvertorParams.ScaledWidth = JPEG_BYTES_PER_PIXEL * JPEG_ConvertorParams.ImageWidth; 
  
  hMCU = (JPEG_ConvertorParams.ImageWidth / JPEG_ConvertorParams.H_factor);
  if((JPEG_ConvertorParams.ImageWidth % JPEG_ConvertorParams.H_factor) != 0)
  {
    hMCU++; /*+1 for horizenatl incomplete MCU */                
  }

  vMCU = (JPEG_ConvertorParams.ImageHeight / JPEG_ConvertorParams.V_factor);
  if((JPEG_ConvertorParams.ImageHeight % JPEG_ConvertorParams.V_factor) != 0)
  {
    vMCU++; /*+1 for vertical incomplete MCU */                
  }
  JPEG_ConvertorParams.MCU_Total_Nb = (hMCU * vMCU);
  *ImageNbMCUs = JPEG_ConvertorParams.MCU_Total_Nb;

  return HAL_OK;
}

/**
  * @brief  Initializes the YCbCr -> RGB colors conversion Look Up Tables  
  * @param  None
  * @retval None
  */
void JPEG_InitPostProcColorTables(void)
{
  int32_t index, i; 
    
  for (i = 0; i <= 255; i++) 
  {
    index = (i * 2) - 256;
    CR_RED_LUT[i] = ( (((int32_t) ((1.40200 / 2) * (1L << 16)))  * index) + ((int32_t) 1 << (16 - 1))) >> 16;
    
    CB_BLUE_LUT[i] = ( (((int32_t) ((1.77200 / 2) * (1L << 16)))  * index) + ((int32_t) 1 << (16 - 1))) >> 16;
    
    CR_GREEN_LUT[i] = (-((int32_t) ((0.71414 / 2) * (1L << 16)))) * index;
    CB_GREEN_LUT[i] = (-((int32_t) ((0.34414 / 2) * (1L << 16)))) * index;
  }  
}
#endif /* USE_JPEG_DECODER == 1 */

/**
  * @brief  Initializes the colors conversion Look Up Tables  
  * @param  None
  * @retval None
  */
void JPEG_InitColorTables(void)
{

#if (USE_JPEG_ENCODER == 1)
  JPEG_InitPreProcColorTables();
  JPEG_Init_MCU_LUT();
#endif

#if (USE_JPEG_DECODER == 1)
  JPEG_InitPostProcColorTables();
#endif

}

#if (USE_JPEG_ENCODER == 1)
/**
  * @brief  Initializes the RGB -> YCbCr colors conversion Look Up Tables  
  * @param  None
  * @retval None
  */
void JPEG_InitPreProcColorTables(void)
{
  int32_t i; 

  for (i = 0; i <= 255; i++) 
  {
    RED_Y_LUT[i]           = ((  ((int32_t) ((0.299 )  * (1L << 16)))  * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
    GREEN_Y_LUT[i]         = ((  ((int32_t) ((0.587 )  * (1L << 16)))  * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
    BLUE_Y_LUT[i]          = ((  ((int32_t) ((0.114 )  * (1L << 16)))  * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
                          
    RED_CB_LUT[i]          = (((-((int32_t) ((0.1687 ) * (1L << 16)))) * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
    GREEN_CB_LUT[i]        = (((-((int32_t) ((0.3313 ) * (1L << 16)))) * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;

    /* BLUE_CB_LUT and RED_CR_LUT are identical */
    BLUE_CB_RED_CR_LUT[i]  = ((  ((int32_t) ((0.5 )    * (1L << 16)))  * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;

    GREEN_CR_LUT[i]        = (((-((int32_t) ((0.4187 ) * (1L << 16)))) * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
    BLUE_CR_LUT[i]         = (((-((int32_t) ((0.0813 ) * (1L << 16)))) * i) + ((int32_t) 1 << (16 - 1))) >> 16 ;
  }  
}

/**
  * @brief  Initializes the MCU Look Up Tables  
  * @param  None
  * @retval None
  */
void JPEG_Init_MCU_LUT(void)
{
  uint32_t i, j, offset;
  
  /*Y LUT */
  for(i = 0; i < 16; i++)
  {
    for(j = 0; j < 16; j++)
    {
      offset =  j + (i*8);  
      if((j>=8) && (i>=8)) offset+= 120;
      else  if((j>=8) && (i<8)) offset+= 56;
      else  if((j<8) && (i>=8)) offset+= 64;
      
      JPEG_Y_MCU_LUT[i*16 + j] = offset;
    }
  }
  
  /*Cb Cr K LUT*/
  for(i = 0; i < 16; i++)
  {
    for(j = 0; j < 16; j++)
    {      
      offset = i*16 + j;
      
      JPEG_Cb_MCU_420_LUT[offset] = (j/2) + ((i/2)*8) + 256;
      JPEG_Cb_MCU_422_LUT[offset] = (j/2) + (i*8) + 128;
      
      JPEG_Cr_MCU_420_LUT[offset] = (j/2) + ((i/2)*8) + 320;
      JPEG_Cr_MCU_422_LUT[offset] = (j/2) + (i*8) + 192;
      
      JPEG_K_MCU_420_LUT[offset] = (j/2) + ((i/2)*8) + 384;
      JPEG_K_MCU_422_LUT[offset] = (j/2) + ((i/2)*8) + 256;
    }
  }

  for(i = 0; i < 8; i++)
  {
    for(j = 0; j < 8; j++)
    {      
      offset = i*8 + j;
      
      JPEG_Y_MCU_444_LUT[offset]  = offset;
      JPEG_Cb_MCU_444_LUT[offset] = offset + 64 ;
      JPEG_Cr_MCU_444_LUT[offset] = offset + 128 ;
      JPEG_K_MCU_444_LUT[offset]  = offset + 192 ;
    }
  }
}

/**
  * @brief  Initializes the YCCK format K Blocks  
  * @param  ChromaSampling
  * @retval pMCUBuffer
  */
static uint8_t *JPEG_Set_K_Blocks(uint8_t *pMCUBuffer, uint8_t pKBlocks[16][16], uint32_t ChromaSampling)
{
  uint32_t i,j;
  
  if(ChromaSampling == JPEG_420_SUBSAMPLING)
  {    
    /* 4:2:0 4Y + Cb + Cr + K*/
    
    pMCUBuffer = pMCUBuffer + 384; 
   /* K block */ 
    for(i=0;i<16;i+=2)
    {
      for(j=0;j<16;j+=2)
      {
        *pMCUBuffer = pKBlocks[i][j];
        pMCUBuffer++;
      }
    }
  }
  else if(ChromaSampling == JPEG_422_SUBSAMPLING)
  {
    /* 4:2:2 2Y horizental + Cb + Cr + K*/
    pMCUBuffer = pMCUBuffer + 256;

    /* K block */
    for(i=0;i<8;i++)
    {
      for(j=0;j<16;j+=2)
      {
        *pMCUBuffer = pKBlocks[i][j];
        pMCUBuffer++;
      }
    }      
  }  
  else if(ChromaSampling == JPEG_444_SUBSAMPLING)
  {
    /* 4:4:4 Y + Cb + Cr + K*/
    
    /* K block */
    pMCUBuffer = pMCUBuffer + 192;
    for(i=0;i<8;i++)
    {
      for(j=0;j<8;j++)
      {
        *pMCUBuffer = pKBlocks[i][j];
        pMCUBuffer++;
      }
    }      
  }
  return pMCUBuffer;
}
#endif /* USE_JPEG_ENCODER == 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
