/**
  ******************************************************************************
  * @file    stm32_lcd.c
  * @author  MCD Application Team
  * @brief   This file includes the basic functionalities to drive LCD
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2019-2020 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* File Info: ------------------------------------------------------------------
                                   User NOTES
1. How To use this driver:
--------------------------
   - This driver is a generic driver that provides basic display services. It should
     be used by any platform once LCD is initialized and following draw methods are
     registered:
         BSP_LCD_DrawBitmap
         BSP_LCD_DrawHLine
         BSP_LCD_DrawVLine
         BSP_LCD_FillRect
         BSP_LCD_ReadPixel
         BSP_LCD_WritePixel
         BSP_LCD_GetXSize
         BSP_LCD_GetYSize
         BSP_LCD_SetActiveLayer

   - At application level, once the LCD is initialized, user should call UTIL_LCD_SetFuncDriver()
     API to link board LCD drivers to BASIC GUI LCD drivers.
     User can then call the BASIC GUI services:
         UTIL_LCD_SetFuncDriver()
         UTIL_LCD_SetLayer()
         UTIL_LCD_SetDevice()
         UTIL_LCD_SetTextColor()
         UTIL_LCD_GetTextColor()
         UTIL_LCD_SetBackColor()
         UTIL_LCD_GetBackColor()
         UTIL_LCD_SetFont()
         UTIL_LCD_GetFont()
         UTIL_LCD_Clear)
         UTIL_LCD_ClearStringLine()
         UTIL_LCD_DisplayStringAtLine()
         UTIL_LCD_DisplayStringAt()
         UTIL_LCD_DisplayChar()
         UTIL_LCD_GetPixel()
         UTIL_LCD_SetPixel()
         UTIL_LCD_FillRGBRect()
         UTIL_LCD_DrawHLine()
         UTIL_LCD_DrawVLine()
         UTIL_LCD_DrawBitmap()
         UTIL_LCD_FillRect()
         UTIL_LCD_DrawLine()
         UTIL_LCD_DrawRect()
         UTIL_LCD_DrawCircle()
         UTIL_LCD_DrawPolygon()
         UTIL_LCD_DrawEllipse()
         UTIL_LCD_FillCircle()
         UTIL_LCD_FillPolygon()
         UTIL_LCD_FillEllipse()
------------------------------------------------------------------------------*/

/* Includes ------------------------------------------------------------------*/
#include "stm32_lcd.h"
#include "../Fonts/font24.c"
#include "../Fonts/font20.c"
#include "../Fonts/font16.c"
#include "../Fonts/font12.c"
#include "../Fonts/font8.c"

/** @addtogroup Utilities
  * @{
  */

/** @addtogroup STM32_EVAL
  * @{
  */

/** @addtogroup Common
  * @{
  */

/** @defgroup STM32_LCD STM32 LCD Utility
  * @{
  */

#ifndef UTIL_LCD_MAX_LAYERS_NBR
  #define UTIL_LCD_MAX_LAYERS_NBR    2U
#endif

/** @defgroup UTIL_LCD_Private_Macros STM32 LCD Utility Private Macros
  * @{
  */
#define ABS(X)                 ((X) > 0 ? (X) : -(X))
#define POLY_X(Z)              ((int32_t)((Points + (Z))->X))
#define POLY_Y(Z)              ((int32_t)((Points + (Z))->Y))

#define CONVERTARGB88882RGB565(Color)((((Color & 0xFFU) >> 3) & 0x1FU) |\
                                     (((((Color & 0xFF00U) >> 8) >>2) & 0x3FU) << 5) |\
                                     (((((Color & 0xFF0000U) >> 16) >>3) & 0x1FU) << 11))

#define CONVERTRGB5652ARGB8888(Color)(((((((Color >> 11) & 0x1FU) * 527) + 23) >> 6) << 16) |\
                                     ((((((Color >> 5) & 0x3FU) * 259) + 33) >> 6) << 8) |\
                                     ((((Color & 0x1FU) * 527) + 23) >> 6) | 0xFF000000)

/**
  * @}
  */

/** @defgroup UTIL_LCD_Private_Types STM32 LCD Utility Private Types
  * @{
  */
typedef struct
{
  uint32_t x1;
  uint32_t y1;
  uint32_t x2;
  uint32_t y2;
  uint32_t x3;
  uint32_t y3;
}Triangle_Positions_t;

/**
  * @}
  */

/** @defgroup UTIL_LCD_Private_Variables STM32 LCD Utility Private Variables
  * @{
  */

/**
  * @brief  Current Drawing Layer properties variable
  */
static UTIL_LCD_Ctx_t DrawProp[UTIL_LCD_MAX_LAYERS_NBR];
static LCD_UTILS_Drv_t FuncDriver;

/**
  * @}
  */

/** @defgroup UTIL_LCD_Private_FunctionPrototypes STM32 LCD Utility Private FunctionPrototypes
  * @{
  */
static void DrawChar(uint32_t Xpos, uint32_t Ypos, const uint8_t *pData);
static void FillTriangle(Triangle_Positions_t *Positions, uint32_t Color);
/**
  * @}
  */

/** @defgroup UTIL_LCD_Exported_Functions STM32 LCD Utility Exported Functions
  * @{
  */

/**
  * @brief  Link board LCD drivers to STM32 LCD Utility drivers
  * @param  pDrv Structure of LCD functions
  */
void UTIL_LCD_SetFuncDriver(const LCD_UTILS_Drv_t *pDrv)
{
  FuncDriver.DrawBitmap     = pDrv->DrawBitmap;
  FuncDriver.FillRGBRect    = pDrv->FillRGBRect;
  FuncDriver.DrawHLine      = pDrv->DrawHLine;
  FuncDriver.DrawVLine      = pDrv->DrawVLine;
  FuncDriver.FillRect       = pDrv->FillRect;
  FuncDriver.GetPixel       = pDrv->GetPixel;
  FuncDriver.SetPixel       = pDrv->SetPixel;
  FuncDriver.GetXSize       = pDrv->GetXSize;
  FuncDriver.GetYSize       = pDrv->GetYSize;
  FuncDriver.SetLayer       = pDrv->SetLayer;
  FuncDriver.GetFormat      = pDrv->GetFormat;

  DrawProp->LcdLayer = 0;
  DrawProp->LcdDevice = 0;
  FuncDriver.GetXSize(0, &DrawProp->LcdXsize);
  FuncDriver.GetYSize(0, &DrawProp->LcdYsize);
  FuncDriver.GetFormat(0, &DrawProp->LcdPixelFormat);
}

/**
  * @brief  Set the LCD layer.
  * @param  Layer  LCD layer
  */
void UTIL_LCD_SetLayer(uint32_t Layer)
{
  if(FuncDriver.SetLayer != NULL)
  {
    if(FuncDriver.SetLayer(DrawProp->LcdDevice, Layer) == 0)
    {
      DrawProp->LcdLayer = Layer;
    }
  }
}

/**
  * @brief  Set the LCD instance to be used.
  * @param  Device  LCD instance
  */
void UTIL_LCD_SetDevice(uint32_t Device)
{
  DrawProp->LcdDevice = Device;
  FuncDriver.GetXSize(Device, &DrawProp->LcdXsize);
  FuncDriver.GetYSize(Device, &DrawProp->LcdYsize);
}

/**
  * @brief  Sets the LCD text color.
  * @param  Color  Text color code
  */
void UTIL_LCD_SetTextColor(uint32_t Color)
{
  DrawProp[DrawProp->LcdLayer].TextColor = Color;
}

/**
  * @brief  Gets the LCD text color.
  * @retval Used text color.
  */
uint32_t UTIL_LCD_GetTextColor(void)
{
  return DrawProp[DrawProp->LcdLayer].TextColor;
}

/**
  * @brief  Sets the LCD background color.
  * @param  Color  Layer background color code
  */
void UTIL_LCD_SetBackColor(uint32_t Color)
{
  DrawProp[DrawProp->LcdLayer].BackColor = Color;
}

/**
  * @brief  Gets the LCD background color.
  * @retval Used background color
  */
uint32_t UTIL_LCD_GetBackColor(void)
{
  return DrawProp[DrawProp->LcdLayer].BackColor;
}

/**
  * @brief  Sets the LCD text font.
  * @param  fonts  Layer font to be used
  */
void UTIL_LCD_SetFont(sFONT *fonts)
{
  DrawProp[DrawProp->LcdLayer].pFont = fonts;
}

/**
  * @brief  Gets the LCD text font.
  * @retval Used layer font
  */
sFONT *UTIL_LCD_GetFont(void)
{
  return DrawProp[DrawProp->LcdLayer].pFont;
}

/**
  * @brief  Draws a RGB rectangle in currently active layer.
  * @param  pData   Pointer to RGB rectangle data
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  Length  Line length
  */
void UTIL_LCD_FillRGBRect(uint32_t Xpos, uint32_t Ypos, uint8_t *pData, uint32_t Width, uint32_t Height)
{
  /* Write RGB rectangle data */
  FuncDriver.FillRGBRect(DrawProp->LcdDevice, Xpos, Ypos, pData, Width, Height);
}

/**
  * @brief  Draws an horizontal line in currently active layer.
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  Length  Line length
  * @param  Color   Draw color
  */
void UTIL_LCD_DrawHLine(uint32_t Xpos, uint32_t Ypos, uint32_t Length, uint32_t Color)
{
  /* Write line */
  if(DrawProp->LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
  {
    FuncDriver.DrawHLine(DrawProp->LcdDevice, Xpos, Ypos, Length, CONVERTARGB88882RGB565(Color));
  }
  else
  {
    FuncDriver.DrawHLine(DrawProp->LcdDevice, Xpos, Ypos, Length, Color);
  }
}

/**
  * @brief  Draws a vertical line in currently active layer.
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  Length  Line length
  * @param  Color   Draw color
  */
void UTIL_LCD_DrawVLine(uint32_t Xpos, uint32_t Ypos, uint32_t Length, uint32_t Color)
{
  /* Write line */
  if(DrawProp->LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
  {
    FuncDriver.DrawVLine(DrawProp->LcdDevice, Xpos, Ypos, Length, CONVERTARGB88882RGB565(Color));
  }
  else
  {
    FuncDriver.DrawVLine(DrawProp->LcdDevice, Xpos, Ypos, Length, Color);
  }
}

/**
  * @brief  Read an LCD pixel.
  * @param  Xpos     X position
  * @param  Ypos     Y position
  * @retval Color    pixel color
  */
void UTIL_LCD_GetPixel(uint16_t Xpos, uint16_t Ypos, uint32_t *Color)
{
  /* Get Pixel */
  FuncDriver.GetPixel(DrawProp->LcdDevice, Xpos, Ypos, Color);
  if(DrawProp->LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
  {
    *Color = CONVERTRGB5652ARGB8888(*Color);
  }
}

/**
  * @brief  Draws a pixel on LCD.
  * @param  Xpos     X position
  * @param  Ypos     Y position
  * @param  Color    Pixel color
  */
void UTIL_LCD_SetPixel(uint16_t Xpos, uint16_t Ypos, uint32_t Color)
{
  /* Set Pixel */
  if(DrawProp->LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
  {
    FuncDriver.SetPixel(DrawProp->LcdDevice, Xpos, Ypos, CONVERTARGB88882RGB565(Color));
  }
  else
  {
    FuncDriver.SetPixel(DrawProp->LcdDevice, Xpos, Ypos, Color);
  }
}

/**
  * @brief  Clears the whole currently active layer of LTDC.
  * @param  Color  Color of the background
  */
void UTIL_LCD_Clear(uint32_t Color)
{
  /* Clear the LCD */
  UTIL_LCD_FillRect(0, 0, DrawProp->LcdXsize, DrawProp->LcdYsize, Color);
}

/**
  * @brief  Clears the selected line in currently active layer.
  * @param  Line  Line to be cleared
  */
void UTIL_LCD_ClearStringLine(uint32_t Line)
{
  /* Draw rectangle with background color */
  UTIL_LCD_FillRect(0, (Line * DrawProp[DrawProp->LcdLayer].pFont->Height), DrawProp->LcdXsize, DrawProp[DrawProp->LcdLayer].pFont->Height, DrawProp[DrawProp->LcdLayer].BackColor);
}

/**
  * @brief  Displays one character in currently active layer.
  * @param  Xpos Start column address
  * @param  Ypos Line where to display the character shape.
  * @param  Ascii Character ascii code
  *           This parameter must be a number between Min_Data = 0x20 and Max_Data = 0x7E
  */
void UTIL_LCD_DisplayChar(uint32_t Xpos, uint32_t Ypos, uint8_t Ascii)
{
  DrawChar(Xpos, Ypos, &DrawProp[DrawProp->LcdLayer].pFont->table[(Ascii-' ') *\
  DrawProp[DrawProp->LcdLayer].pFont->Height * ((DrawProp[DrawProp->LcdLayer].pFont->Width + 7) / 8)]);
}

/**
  * @brief  Displays characters in currently active layer.
  * @param  Xpos X position (in pixel)
  * @param  Ypos Y position (in pixel)
  * @param  Text Pointer to string to display on LCD
  * @param  Mode Display mode
  *          This parameter can be one of the following values:
  *            @arg  CENTER_MODE
  *            @arg  RIGHT_MODE
  *            @arg  LEFT_MODE
  */
void UTIL_LCD_DisplayStringAt(uint32_t Xpos, uint32_t Ypos, uint8_t *Text, Text_AlignModeTypdef Mode)
{
  uint32_t refcolumn = 1, i = 0;
  uint32_t size = 0, xsize = 0;
  uint8_t  *ptr = Text;

  /* Get the text size */
  while (*ptr++) size ++ ;

  /* Characters number per line */
  xsize = (DrawProp->LcdXsize/DrawProp[DrawProp->LcdLayer].pFont->Width);

  switch (Mode)
  {
  case CENTER_MODE:
    {
      refcolumn = Xpos + ((xsize - size)* DrawProp[DrawProp->LcdLayer].pFont->Width) / 2;
      break;
    }
  case LEFT_MODE:
    {
      refcolumn = Xpos;
      break;
    }
  case RIGHT_MODE:
    {
      refcolumn = - Xpos + ((xsize - size)*DrawProp[DrawProp->LcdLayer].pFont->Width);
      break;
    }
  default:
    {
      refcolumn = Xpos;
      break;
    }
  }

  /* Check that the Start column is located in the screen */
  if ((refcolumn < 1) || (refcolumn >= 0x8000))
  {
    refcolumn = 1;
  }

  /* Send the string character by character on LCD */
  while ((*Text != 0) & (((DrawProp->LcdXsize - (i*DrawProp[DrawProp->LcdLayer].pFont->Width)) & 0xFFFF) >= DrawProp[DrawProp->LcdLayer].pFont->Width))
  {
    /* Display one character on LCD */
    UTIL_LCD_DisplayChar(refcolumn, Ypos, *Text);
    /* Decrement the column position by 16 */
    refcolumn += DrawProp[DrawProp->LcdLayer].pFont->Width;

    /* Point on the next character */
    Text++;
    i++;
  }
}

/**
  * @brief  Displays a maximum of 60 characters on the LCD.
  * @param  Line: Line where to display the character shape
  * @param  ptr: Pointer to string to display on LCD
  */
void UTIL_LCD_DisplayStringAtLine(uint32_t Line, uint8_t *ptr)
{
  UTIL_LCD_DisplayStringAt(0, LINE(Line), ptr, LEFT_MODE);
}

/**
  * @brief  Draws an uni-line (between two points) in currently active layer.
  * @param  Xpos1 Point 1 X position
  * @param  Ypos1 Point 1 Y position
  * @param  Xpos2 Point 2 X position
  * @param  Ypos2 Point 2 Y position
  * @param  Color Draw color
  */
void UTIL_LCD_DrawLine(uint32_t Xpos1, uint32_t Ypos1, uint32_t Xpos2, uint32_t Ypos2, uint32_t Color)
{
  int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
  yinc1 = 0, yinc2 = 0, den = 0, num = 0, numadd = 0, numpixels = 0,
  curpixel = 0;
  int32_t x_diff, y_diff;

  x_diff = Xpos2 - Xpos1;
  y_diff = Ypos2 - Ypos1;

  deltax = ABS(x_diff);         /* The absolute difference between the x's */
  deltay = ABS(y_diff);         /* The absolute difference between the y's */
  x = Xpos1;                       /* Start x off at the first pixel */
  y = Ypos1;                       /* Start y off at the first pixel */

  if (Xpos2 >= Xpos1)                 /* The x-values are increasing */
  {
    xinc1 = 1;
    xinc2 = 1;
  }
  else                          /* The x-values are decreasing */
  {
    xinc1 = -1;
    xinc2 = -1;
  }

  if (Ypos2 >= Ypos1)                 /* The y-values are increasing */
  {
    yinc1 = 1;
    yinc2 = 1;
  }
  else                          /* The y-values are decreasing */
  {
    yinc1 = -1;
    yinc2 = -1;
  }

  if (deltax >= deltay)         /* There is at least one x-value for every y-value */
  {
    xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
    yinc2 = 0;                  /* Don't change the y for every iteration */
    den = deltax;
    num = deltax / 2;
    numadd = deltay;
    numpixels = deltax;         /* There are more x-values than y-values */
  }
  else                          /* There is at least one y-value for every x-value */
  {
    xinc2 = 0;                  /* Don't change the x for every iteration */
    yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
    den = deltay;
    num = deltay / 2;
    numadd = deltax;
    numpixels = deltay;         /* There are more y-values than x-values */
  }

  for (curpixel = 0; curpixel <= numpixels; curpixel++)
  {
    UTIL_LCD_SetPixel(x, y, Color);   /* Draw the current pixel */
    num += numadd;                            /* Increase the numerator by the top of the fraction */
    if (num >= den)                           /* Check if numerator >= denominator */
    {
      num -= den;                             /* Calculate the new numerator value */
      x += xinc1;                             /* Change the x as appropriate */
      y += yinc1;                             /* Change the y as appropriate */
    }
    x += xinc2;                               /* Change the x as appropriate */
    y += yinc2;                               /* Change the y as appropriate */
  }
}

/**
  * @brief  Draws a rectangle in currently active layer.
  * @param  Xpos X position
  * @param  Ypos Y position
  * @param  Width  Rectangle width
  * @param  Height Rectangle height
  * @param  Color  Draw color
  */
void UTIL_LCD_DrawRect(uint32_t Xpos, uint32_t Ypos, uint32_t Width, uint32_t Height, uint32_t Color)
{
  /* Draw horizontal lines */
  UTIL_LCD_DrawHLine(Xpos, Ypos, Width, Color);
  UTIL_LCD_DrawHLine(Xpos, (Ypos+ Height - 1U), Width, Color);

  /* Draw vertical lines */
  UTIL_LCD_DrawVLine(Xpos, Ypos, Height, Color);
  UTIL_LCD_DrawVLine((Xpos + Width - 1U), Ypos, Height, Color);
}

/**
  * @brief  Draws a circle in currently active layer.
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  Radius  Circle radius
  * @param  Color   Draw color
  */
void UTIL_LCD_DrawCircle(uint32_t Xpos, uint32_t Ypos, uint32_t Radius, uint32_t Color)
{
  int32_t   decision;  /* Decision Variable */
  uint32_t  current_x; /* Current X Value */
  uint32_t  current_y; /* Current Y Value */

  decision = 3 - (Radius << 1);
  current_x = 0;
  current_y = Radius;

  while (current_x <= current_y)
  {
    if((Ypos - current_y) < DrawProp->LcdYsize)
    {
      if((Xpos + current_x) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos + current_x), (Ypos - current_y), Color);
      }
      if((Xpos - current_x) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos - current_x), (Ypos - current_y), Color);
      }
    }

    if((Ypos - current_x) < DrawProp->LcdYsize)
    {
      if((Xpos + current_y) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos + current_y), (Ypos - current_x), Color);
      }
      if((Xpos - current_y) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos - current_y), (Ypos - current_x), Color);
      }
    }

    if((Ypos + current_y) < DrawProp->LcdYsize)
    {
      if((Xpos + current_x) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos + current_x), (Ypos + current_y), Color);
      }
      if((Xpos - current_x) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos - current_x), (Ypos + current_y), Color);
      }
    }

    if((Ypos + current_x) < DrawProp->LcdYsize)
    {
      if((Xpos + current_y) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos + current_y), (Ypos + current_x), Color);
      }
      if((Xpos - current_y) < DrawProp->LcdXsize)
      {
        UTIL_LCD_SetPixel((Xpos - current_y), (Ypos + current_x), Color);
      }
    }

    if (decision < 0)
    {
      decision += (current_x << 2) + 6;
    }
    else
    {
      decision += ((current_x - current_y) << 2) + 10;
      current_y--;
    }
    current_x++;
  }
}

/**
  * @brief  Draws an poly-line (between many points) in currently active layer.
  * @param  Points      Pointer to the points array
  * @param  PointCount  Number of points
  * @param  Color       Draw color
  */
void UTIL_LCD_DrawPolygon(pPoint Points, uint32_t PointCount, uint32_t Color)
{
  int16_t x_pos = 0, y_pos = 0;

  if(PointCount < 2)
  {
    return;
  }

  UTIL_LCD_DrawLine(Points->X, Points->Y, (Points+PointCount-1)->X, (Points+PointCount-1)->Y, Color);

  while(--PointCount)
  {
    x_pos = Points->X;
    y_pos = Points->Y;
    Points++;
    UTIL_LCD_DrawLine(x_pos, y_pos, Points->X, Points->Y, Color);
  }
}

/**
  * @brief  Draws an ellipse on LCD in currently active layer.
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  XRadius Ellipse X radius
  * @param  YRadius Ellipse Y radius
  * @param  Color   Draw color
  */
void UTIL_LCD_DrawEllipse(int Xpos, int Ypos, int XRadius, int YRadius, uint32_t Color)
{
  int x_pos = 0, y_pos = -YRadius, err = 2-2*XRadius, e2;
  float k = 0, rad1 = 0, rad2 = 0;

  rad1 = XRadius;
  rad2 = YRadius;

  k = (float)(rad2/rad1);

  do
  {
    UTIL_LCD_SetPixel((Xpos-(uint32_t)(x_pos/k)), (Ypos + y_pos), Color);
    UTIL_LCD_SetPixel((Xpos+(uint32_t)(x_pos/k)), (Ypos + y_pos), Color);
    UTIL_LCD_SetPixel((Xpos+(uint32_t)(x_pos/k)), (Ypos - y_pos), Color);
    UTIL_LCD_SetPixel((Xpos-(uint32_t)(x_pos/k)), (Ypos - y_pos), Color);

    e2 = err;
    if (e2 <= x_pos)
    {
      err += ++x_pos*2+1;
      if (-y_pos == x_pos && e2 <= y_pos) e2 = 0;
    }
    if (e2 > y_pos)
    {
      err += ++y_pos*2+1;
    }
  }while (y_pos <= 0);
}

/**
  * @brief  Draws a bitmap picture loaded in the internal Flash (32 bpp) in currently active layer.
  * @param  Xpos  Bmp X position in the LCD
  * @param  Ypos  Bmp Y position in the LCD
  * @param  pData Pointer to Bmp picture address in the internal Flash
  */
void UTIL_LCD_DrawBitmap(uint32_t Xpos, uint32_t Ypos, uint8_t *pData)
{
  FuncDriver.DrawBitmap(DrawProp->LcdDevice, Xpos, Ypos, pData);
}

/**
  * @brief  Draws a full rectangle in currently active layer.
  * @param  Xpos   X position
  * @param  Ypos   Y position
  * @param  Width  Rectangle width
  * @param  Height Rectangle height
  * @param  Color  Draw color
  */
void UTIL_LCD_FillRect(uint32_t Xpos, uint32_t Ypos, uint32_t Width, uint32_t Height, uint32_t Color)
{
  /* Fill the rectangle */
  if(DrawProp->LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
  {
    FuncDriver.FillRect(DrawProp->LcdDevice, Xpos, Ypos, Width, Height, CONVERTARGB88882RGB565(Color));
  }
  else
  {
    FuncDriver.FillRect(DrawProp->LcdDevice, Xpos, Ypos, Width, Height, Color);
  }
}

/**
  * @brief  Draws a full circle in currently active layer.
  * @param  Xpos   X position
  * @param  Ypos   Y position
  * @param  Radius Circle radius
  * @param  Color  Draw color
  */
void UTIL_LCD_FillCircle(uint32_t Xpos, uint32_t Ypos, uint32_t Radius, uint32_t Color)
{
  int32_t   decision;  /* Decision Variable */
  uint32_t  current_x; /* Current X Value */
  uint32_t  current_y; /* Current Y Value */

  decision = 3 - (Radius << 1);

  current_x = 0;
  current_y = Radius;

  while (current_x <= current_y)
  {
    if(current_y > 0)
    {
      if(current_y >= Xpos)
      {
        UTIL_LCD_DrawHLine(0, Ypos + current_x, 2*current_y - (current_y - Xpos), Color);
        UTIL_LCD_DrawHLine(0, Ypos - current_x, 2*current_y - (current_y - Xpos), Color);
      }
      else
      {
        UTIL_LCD_DrawHLine(Xpos - current_y, Ypos + current_x, 2*current_y, Color);
        UTIL_LCD_DrawHLine(Xpos - current_y, Ypos - current_x, 2*current_y, Color);
      }
    }

    if(current_x > 0)
    {
      if(current_x >= Xpos)
      {
        UTIL_LCD_DrawHLine(0, Ypos - current_y, 2*current_x - (current_x - Xpos), Color);
        UTIL_LCD_DrawHLine(0, Ypos + current_y, 2*current_x - (current_x - Xpos), Color);
      }
      else
      {
        UTIL_LCD_DrawHLine(Xpos - current_x, Ypos - current_y, 2*current_x, Color);
        UTIL_LCD_DrawHLine(Xpos - current_x, Ypos + current_y, 2*current_x, Color);
      }
    }
    if (decision < 0)
    {
      decision += (current_x << 2) + 6;
    }
    else
    {
      decision += ((current_x - current_y) << 2) + 10;
      current_y--;
    }
    current_x++;
  }

  UTIL_LCD_DrawCircle(Xpos, Ypos, Radius, Color);
}

/**
  * @brief  Draws a full poly-line (between many points) in currently active layer.
  * @param  Points     Pointer to the points array
  * @param  PointCount Number of points
  * @param  Color      Draw color
  */
void UTIL_LCD_FillPolygon(pPoint Points, uint32_t PointCount, uint32_t Color)
{
  int16_t X = 0, Y = 0, X2 = 0, Y2 = 0, x_center = 0, y_center = 0, x_first = 0, y_first = 0, pixel_x = 0, pixel_y = 0, counter = 0;
  uint32_t  image_left = 0, image_right = 0, image_top = 0, image_bottom = 0;
  Triangle_Positions_t positions;

  image_left = image_right = Points->X;
  image_top= image_bottom = Points->Y;

  for(counter = 1; counter < PointCount; counter++)
  {
    pixel_x = POLY_X(counter);
    if(pixel_x < image_left)
    {
      image_left = pixel_x;
    }
    if(pixel_x > image_right)
    {
      image_right = pixel_x;
    }

    pixel_y = POLY_Y(counter);
    if(pixel_y < image_top)
    {
      image_top = pixel_y;
    }
    if(pixel_y > image_bottom)
    {
      image_bottom = pixel_y;
    }
  }

  if(PointCount < 2)
  {
    return;
  }

  x_center = (image_left + image_right)/2;
  y_center = (image_bottom + image_top)/2;

  x_first = Points->X;
  y_first = Points->Y;

  while(--PointCount)
  {
    X = Points->X;
    Y = Points->Y;
    Points++;
    X2 = Points->X;
    Y2 = Points->Y;
    positions.x1 = X;
    positions.y1 = Y;
    positions.x2 = X2;
    positions.y2 = Y2;
    positions.x3 = x_center;
    positions.y3 = y_center;
    FillTriangle(&positions, Color);

    positions.x2 = x_center;
    positions.y2 = y_center;
    positions.x3 = X2;
    positions.y3 = Y2;
    FillTriangle(&positions, Color);

    positions.x1 = x_center;
    positions.y1 = y_center;
    positions.x2 = X2;
    positions.y2 = Y2;
    positions.x3 = X;
    positions.y3 = Y;
    FillTriangle(&positions, Color);
  }

    positions.x1 = x_first;
    positions.y1 = y_first;
    positions.x2 = X2;
    positions.y2 = Y2;
    positions.x3 = x_center;
    positions.y3 = y_center;
    FillTriangle(&positions, Color);

    positions.x2 = x_center;
    positions.y2 = y_center;
    positions.x3 = X2;
    positions.y3 = Y2;
    FillTriangle(&positions, Color);

    positions.x1 = x_center;
    positions.y1 = y_center;
    positions.x2 = X2;
    positions.y2 = Y2;
    positions.x3 = x_first;
    positions.y3 = y_first;
    FillTriangle(&positions, Color);
}

/**
  * @brief  Draws a full ellipse in currently active layer.
  * @param  Xpos    X position
  * @param  Ypos    Y position
  * @param  XRadius Ellipse X radius
  * @param  YRadius Ellipse Y radius
  * @param  Color   Draw color
  */
void UTIL_LCD_FillEllipse(int Xpos, int Ypos, int XRadius, int YRadius, uint32_t Color)
{
  int x_pos = 0, y_pos = -YRadius, err = 2-2*XRadius, e2;
  float k = 0, rad1 = 0, rad2 = 0;

  rad1 = XRadius;
  rad2 = YRadius;

  k = (float)(rad2/rad1);

  do
  {
    UTIL_LCD_DrawHLine((Xpos-(uint32_t)(x_pos/k)), (Ypos + y_pos), (2*(uint32_t)(x_pos/k) + 1), Color);
    UTIL_LCD_DrawHLine((Xpos-(uint32_t)(x_pos/k)), (Ypos - y_pos), (2*(uint32_t)(x_pos/k) + 1), Color);

    e2 = err;
    if (e2 <= x_pos)
    {
      err += ++x_pos*2+1;
      if (-y_pos == x_pos && e2 <= y_pos) e2 = 0;
    }
    if (e2 > y_pos) err += ++y_pos*2+1;
  }
  while (y_pos <= 0);
}

/**
  * @brief  Draws a character on LCD.
  * @param  Xpos  Line where to display the character shape
  * @param  Ypos  Start column address
  * @param  pData Pointer to the character data
  */
static void DrawChar(uint32_t Xpos, uint32_t Ypos, const uint8_t *pData)
{
  uint32_t i = 0, j = 0, offset;
  uint32_t height, width;
  uint8_t  *pchar;
  uint32_t line;

  height = DrawProp[DrawProp->LcdLayer].pFont->Height;
  width  = DrawProp[DrawProp->LcdLayer].pFont->Width;
  uint16_t rgb565[24];
  uint32_t argb8888[24];

  offset =  8 *((width + 7)/8) -  width ;

  for(i = 0; i < height; i++)
  {
    pchar = ((uint8_t *)pData + (width + 7)/8 * i);

    switch(((width + 7)/8))
    {

    case 1:
      line =  pchar[0];
      break;

    case 2:
      line =  (pchar[0]<< 8) | pchar[1];
      break;

    case 3:
    default:
      line =  (pchar[0]<< 16) | (pchar[1]<< 8) | pchar[2];
      break;
    }

    if(DrawProp[DrawProp->LcdLayer].LcdPixelFormat == LCD_PIXEL_FORMAT_RGB565)
    {
      for (j = 0; j < width; j++)
      {
        if(line & (1 << (width- j + offset- 1)))
        {
          rgb565[j] = CONVERTARGB88882RGB565(DrawProp[DrawProp->LcdLayer].TextColor);
        }
        else
        {
          rgb565[j] = CONVERTARGB88882RGB565(DrawProp[DrawProp->LcdLayer].BackColor);
        }
      }
      UTIL_LCD_FillRGBRect(Xpos,  Ypos++, (uint8_t*)&rgb565[0], width, 1);
    }
    else
    {
      for (j = 0; j < width; j++)
      {
        if(line & (1 << (width- j + offset- 1)))
        {
          argb8888[j] = DrawProp[DrawProp->LcdLayer].TextColor;
        }
        else
        {
          argb8888[j] = DrawProp[DrawProp->LcdLayer].BackColor;
        }
      }
      UTIL_LCD_FillRGBRect(Xpos,  Ypos++, (uint8_t*)&argb8888[0], width, 1);
    }
  }
}

/**
  * @brief  Fills a triangle (between 3 points).
  * @param  Positions  pointer to riangle coordinates
  * @param  Color      Draw color
  */
static void FillTriangle(Triangle_Positions_t *Positions, uint32_t Color)
{
  int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
  yinc1 = 0, yinc2 = 0, den = 0, num = 0, numadd = 0, numpixels = 0,
  curpixel = 0;
  int32_t x_diff, y_diff;

  x_diff = Positions->x2 - Positions->x1;
  y_diff = Positions->y2 - Positions->y1;

  deltax = ABS(x_diff);         /* The absolute difference between the x's */
  deltay = ABS(y_diff);         /* The absolute difference between the y's */
  x = Positions->x1;                       /* Start x off at the first pixel */
  y = Positions->y1;                       /* Start y off at the first pixel */

  if (Positions->x2 >= Positions->x1)                 /* The x-values are increasing */
  {
    xinc1 = 1;
    xinc2 = 1;
  }
  else                          /* The x-values are decreasing */
  {
    xinc1 = -1;
    xinc2 = -1;
  }

  if (Positions->y2 >= Positions->y1)                 /* The y-values are increasing */
  {
    yinc1 = 1;
    yinc2 = 1;
  }
  else                          /* The y-values are decreasing */
  {
    yinc1 = -1;
    yinc2 = -1;
  }

  if (deltax >= deltay)         /* There is at least one x-value for every y-value */
  {
    xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
    yinc2 = 0;                  /* Don't change the y for every iteration */
    den = deltax;
    num = deltax / 2;
    numadd = deltay;
    numpixels = deltax;         /* There are more x-values than y-values */
  }
  else                          /* There is at least one y-value for every x-value */
  {
    xinc2 = 0;                  /* Don't change the x for every iteration */
    yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
    den = deltay;
    num = deltay / 2;
    numadd = deltax;
    numpixels = deltay;         /* There are more y-values than x-values */
  }

  for (curpixel = 0; curpixel <= numpixels; curpixel++)
  {
    UTIL_LCD_DrawLine(x, y, Positions->x3, Positions->y3, Color);

    num += numadd;              /* Increase the numerator by the top of the fraction */
    if (num >= den)             /* Check if numerator >= denominator */
    {
      num -= den;               /* Calculate the new numerator value */
      x += xinc1;               /* Change the x as appropriate */
      y += yinc1;               /* Change the y as appropriate */
    }
    x += xinc2;                 /* Change the x as appropriate */
    y += yinc2;                 /* Change the y as appropriate */
  }
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */
