#include "bsp_lcd.h"
#include "elog.h"

LCD_TypeDef cur_lcd = INCH_4_3;
LCD_DrawPropTypeDef DrawProp[2];

MY_LCD_TypeDef my_lcd = {

	0, 0, //   宽度 高度

	0, 4, // 横屏还是竖屏控制：0，竖屏；1，横屏,像素大小。

	LTDC_PIXEL_FORMAT_ARGB8888, // PIXEL Format
	LCD_COLOR_WHITE,    		// color
	LCD_COLOR_BLACK,    		// back_color
	NULL,						// LCD_DrawPropTypeDef draw_prop[2];
	0,   						// show_num_mode

	0,	//active layer
	{LCD_BUF_ADDRESS,LCD_BUF_ADDRESS},  // Layer0 Addr
};

// static void LCD_Draw_Point(uint16_t x, uint16_t y, uint32_t color);
// static void DrawChar(uint16_t Xpos, uint16_t Ypos, const uint8_t *c);
extern void DrawChar(uint16_t Xpos, uint16_t Ypos, const uint8_t *c);

void LCD_Init()
{

	LTDC_Init();
	// TODO my_lcd  调用LTDC画点，无需考虑坐标切换
	LCD_DisplayDirection(1);

#if LCD_PIXFORMAT == LTDC_PIXEL_FORMAT_ARGB8888 || LCD_PIXFORMAT == LTDC_PIXEL_FORMAT_RGB888
	my_lcd.pixelsize = 4; // 每个像素占4个字节
#else
	my_lcd.pixelsize = 2; // 每个像素占2个字节
#endif
	my_lcd.layeraddr[0] = LCD_BUF_ADDRESS;
	my_lcd.layeraddr[1] = LCD_BUF_ADDRESS + my_lcd.width * my_lcd.height * my_lcd.pixelsize;
	
	LCD_SetLayer(0, my_lcd.layeraddr[0] , my_lcd.color_format, LCD_COLOR_GREEN,255);
	LCD_SetLayer(1, my_lcd.layeraddr[1], my_lcd.color_format, LCD_COLOR_GREEN,0);
	LCD_Set_Active_layer(0);
    LCD_Clear(LTDC_COLOR_GREEN);			//清屏
	LTDC_DISP_ON();
	// LTDC_Display_Dir(1);
	// my_lcd.width = my_ltdc.width;
	// my_lcd.height = my_ltdc.height;
	// my_lcd.activelayer = my_ltdc.activelayer;

	// TODO 添加 DrawProp[2] 定义
	my_lcd.draw_prop[0].BackColor = LCD_COLOR_WHITE; // 设置层的字体颜色
	my_lcd.draw_prop[0].pFont = &Font16;	 // 设置层的字体类型
	my_lcd.draw_prop[0].pCnFont = &CH_Font16;	 // 设置层的字体类型
	my_lcd.draw_prop[0].TextColor = LCD_COLOR_BLACK; // 设置层的字体背景颜色
	my_lcd.draw_prop[1].BackColor = LCD_COLOR_WHITE; // 设置层的字体颜色
	my_lcd.draw_prop[1].pFont = &Font16;	 // 设置层的字体类型
	my_lcd.draw_prop[1].pCnFont = &CH_Font16;	 // 设置层的字体类型
	my_lcd.draw_prop[1].TextColor = LCD_COLOR_BLACK; // 设置层的字体背景颜色
}

void LCD_DisplayDirection(uint8_t dir){
    my_lcd.dir = dir;
	// my_ltdc.dir = dir; // 显示方向

	if(dir==0)			//竖屏
	{
		my_lcd.width=my_ltdc.pixel_height;
		my_lcd.height=my_ltdc.pixel_width;	
	}else if(dir==1)	//横屏
	{
		my_lcd.width=my_ltdc.pixel_width;
		my_lcd.height=my_ltdc.pixel_height;
	}
}

void LCD_Set_Active_layer( uint8_t layer_no )
{
	my_lcd.activelayer = layer_no;
	my_ltdc.activelayer = layer_no;
}

void LCD_SetLayer( uint8_t layer,uint32_t buf_addr, uint32_t layer_color_format,
				 uint32_t layer_back_color,uint8_t alpha )
{

	LTDC_Layer_Parameter_Config( layer,buf_addr,my_lcd.color_format,alpha,
				0,6,7,LCD_COLOR_GREEN);//层参数配置
	LTDC_Layer_Window_Config(layer,0,0,my_ltdc.pixel_width,my_ltdc.pixel_height);	//层窗口配置,以LCD面板坐标系为基准,不要随便修改!
}  

/**
 * @brief  设置LCD当前层文字颜色
 * @param  Color: 文字颜色
 * @retval 无
 */
void LCD_SetTextColor(uint32_t Color)
{
	my_lcd.draw_prop[my_lcd.activelayer].TextColor = Color;
}

/**
  * @brief  获取LCD当前层文字颜色
  * @retval 文字颜色
  */
uint32_t LCD_GetTextColor(void)
{
  return my_lcd.draw_prop[my_lcd.activelayer].TextColor;
}

/**
  * @brief  设置LCD当前层的文字背景颜色
  * @param  Color: 文字背景颜色
  * @retval 无
  */
void LCD_SetBackColor(uint32_t Color)
{
  my_lcd.draw_prop[my_lcd.activelayer].BackColor = Color;
}

/**
  * @brief  获取LCD当前层的文字背景颜色
  * @retval 文字背景颜色
  */
uint32_t LCD_GetBackColor(void)
{
  return my_lcd.draw_prop[my_lcd.activelayer].BackColor;
}

/**
 * @brief  设置LCD文字的颜色和背景的颜色
 * @param  TextColor: 指定文字颜色
 * @param  BackColor: 指定背景颜色
 * @retval 无
 */
void LCD_SetColors(uint32_t TextColor, uint32_t BackColor)
{
     LCD_SetTextColor (TextColor);
     LCD_SetBackColor (BackColor);
}

/**
  * @brief  设置LCD当前层显示的字体
  * @param  fonts: 字体类型
  * @retval None
  */
void LCD_SetFont(sFONT *fonts)
{
  my_lcd.draw_prop[my_lcd.activelayer].pFont = fonts;
}

/**
  * @brief  获取LCD当前层显示的字体
  * @retval 字体类型
  */
sFONT *LCD_GetFont(void)
{
  return my_lcd.draw_prop[my_lcd.activelayer].pFont;
}
/*******************************************************************************
							局部函数
*******************************************************************************/
// static void LCD_Draw_Point(uint16_t x, uint16_t y, uint32_t color)
// {
// 	LTDC_Draw_Point(x, y, color);
// }

//画点函数
//x,y:写入坐标
//color:颜色值
void LCD_Draw_Point(uint16_t x,uint16_t y,uint32_t color)
{ 

	if(my_lcd.dir)	//横屏
	{
        *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_lcd.pixelsize*(my_lcd.width*y+x))=color;
	}else 			//竖屏
	{
        *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_lcd.pixelsize*(my_lcd.height*(my_lcd.width-x)+y))=color; 
        // *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_lcd.pixelsize*(my_ltdc.pixel_width*(my_ltdc.pixel_height-x)+y))=color; 
	}

}

//读点函数
//x,y:读取点的坐标
//返回值:颜色值
uint32_t LCD_Read_Point(uint16_t x,uint16_t y)
{ 
		// return *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_ltdc.pixelsize*(my_ltdc.pixel_width*y+x));
	if(my_lcd.dir)	//横屏
	{
		return *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_lcd.pixelsize*(my_lcd.width*y+x));
	}else 			//竖屏
	{
		return *(uint32_t*)((uint32_t)my_lcd.layeraddr[my_lcd.activelayer]+my_lcd.pixelsize*(my_lcd.height*(my_lcd.width-x)+y)); 
	}
}



/**
  * @brief  显示一个字符
  * @param  Xpos: 显示字符的行位置
  * @param  Ypos: 列起始位置
  * @param  c: 指向字体数据的指针
  * @retval 无
  */
 void DrawChar(uint16_t Xpos, uint16_t Ypos, const uint8_t *c)
{
	uint32_t i = 0, j = 0;
	uint16_t height, width;
	uint8_t  offset;
	uint8_t  *pchar;
	uint8_t  tempchar;
	uint32_t line;
	uint8_t t,t1;
	uint8_t y0 = Ypos;
	
	height = my_lcd.draw_prop[my_lcd.activelayer].pFont->Height;//获取正在使用字体高度
	width  = my_lcd.draw_prop[my_lcd.activelayer].pFont->Width; //获取正在使用字体宽度
	
	offset =  8 *((width + 7)/8) -  width ;//计算字符的每一行像素的偏移值，实际存储大小-字体宽度

	for(i = 0; i < height; i++)//遍历字体高度绘点
	{
		pchar = ((uint8_t *)c + (width + 7)/8 * i);//计算字符的每一行像素的偏移地址
		switch( ((width + 7)/8) )//根据字体宽度来提取不同字体的实际像素值
		{
			case 1:
			line =  pchar[0];      //提取字体宽度小于8的字符的像素值
			break;
			case 2:
			line =  (pchar[0]<< 8) | pchar[1]; //提取字体宽度大于8小于16的字符的像素值     
			break;
			case 3:
			default:
			line =  (pchar[0]<< 16) | (pchar[1]<< 8) | pchar[2]; //提取字体宽度大于16小于24的字符的像素值     
			break;
		}

		for (j = 0; j < width; j++)//遍历字体宽度绘点
		{
			if( line & (1 << (width- j + offset- 1)) )	//根据每一行的像素值及偏移位置按照当前字体颜色进行绘点
			{
				LCD_Draw_Point((Xpos + j), Ypos, my_lcd.draw_prop[my_lcd.activelayer].TextColor);
			}
			else	//如果这一行没有字体像素则按照背景颜色绘点
			{
				LCD_Draw_Point((Xpos + j), Ypos, my_lcd.draw_prop[my_lcd.activelayer].BackColor);
			} 
		}
		Ypos++;
	}
}

/***************************************************************************************************************
*	函 数 名:	LCD_DisplayChar
*
*	入口参数:	x - 起始水平坐标，取值范围0~799 
*					y - 起始垂直坐标，取值范围0~479
*					c  - ASCII字符
*
*	函数功能:	在指定坐标显示指定的字符
*
*	说    明:	1. 可设置要显示的字体，例如使用 LCD_SetFont(&Font24) 设置为 2412的ASCII字体
*					2.	可设置要显示的颜色，例如使用 LCD_SetColor(0xff0000FF) 设置为蓝色
*					3. 可设置对应的背景色，例如使用 LCD_SetBackColor(0xff000000) 设置为黑色的背景色
*					4. 使用示例 LCD_DisplayChar( 10, 10, 'a') ，在坐标(10,10)显示字符 'a'
*
***************************************************************************************************************/

/**
  * @brief  显示一个字符
  * @param  Xpos: X轴起始坐标
  * @param  Ypos: Y轴起始坐标
  * @param  Ascii: 字符 ascii 码,范围（ 0x20 —0x7E ）
  * @retval 无
  */
void LCD_DisplayChar(uint16_t Xpos, uint16_t Ypos, uint8_t Ascii)
{

    // DrawChar(Xpos, Ypos, &DrawProp[ActiveLayer].pFont->table[(Ascii-' ') *\
    // DrawProp[ActiveLayer].pFont->Height * ((DrawProp[ActiveLayer].pFont->Width + 7) / 8)]);

    DrawChar(Xpos, Ypos, &my_lcd.draw_prop[my_lcd.activelayer].pFont->table[(Ascii-' ') *	\
			my_lcd.draw_prop[my_lcd.activelayer].pFont->Height * 							\
			((my_lcd.draw_prop[my_lcd.activelayer].pFont->Width + 7) / 8)] );
}

/**
  * @brief  显示字符串
  * @param  Xpos: X轴起始坐标
  * @param  Ypos: Y轴起始坐标 
  * @param  Text: 字符串指针
  * @param  Mode: 显示对齐方式，可以是CENTER_MODE、RIGHT_MODE、LEFT_MODE
  * @retval None
  */
void LCD_DisplayStringAt(uint16_t Xpos, uint16_t Ypos, uint8_t *Text, Text_AlignModeTypdef Mode)
{
	uint16_t ref_column = 1, i = 0;
	uint32_t size = 0, xsize = 0; 
	uint8_t  *ptr = Text;
	
	/* 获取字符串大小 */
	while (*ptr++) size ++ ;
	
	/* 每一行可以显示字符的数量 */
	xsize = (my_lcd.width/my_lcd.draw_prop[my_lcd.activelayer].pFont->Width);
	
	switch (Mode)
	{
	case CENTER_MODE:
		{
		ref_column = Xpos + ((xsize - size)* my_lcd.draw_prop[my_lcd.activelayer].pFont->Width) / 2;
		break;
		}
	case LEFT_MODE:
		{
		ref_column = Xpos;
		break;
		}
	case RIGHT_MODE:
		{
		ref_column = - Xpos + ((xsize - size)*my_lcd.draw_prop[my_lcd.activelayer].pFont->Width);
		break;
		}    
	default:
		{
		ref_column = Xpos;
		break;
		}
	}
	
	/*检查起始行是否在显示范围内 */
	if ((ref_column < 1) || (ref_column >= 0x8000))
	{
		ref_column = 1;
	}

	/* 使用字符显示函数显示每一个字符*/
	while ( (*Text != 0) & (((my_lcd.width - (i*my_lcd.draw_prop[my_lcd.activelayer].pFont->Width)) & 0xFFFF)  \
				>= my_lcd.draw_prop[my_lcd.activelayer].pFont->Width) )
	{
		/* 显示一个字符 */
		LCD_DisplayChar( ref_column, Ypos, *Text );
		/* 根据字体大小计算下一个偏移位置 */
		ref_column += my_lcd.draw_prop[my_lcd.activelayer].pFont->Width;
		/* 指针指向下一个字符 */
		Text++;
		i++;
	}  
}

/**
  * @brief  在指定行显示字符串(最多60个)
  * @param  Line: 显示的行
  * @param  ptr: 字符串指针
  * @retval 无
  */
void LCD_DisplayStringLine(uint16_t Line, uint8_t *ptr)
{  
	LCD_DisplayStringAt(0, LINE(Line), ptr, LEFT_MODE);
}


void  LCD_DisplayNumber( uint16_t Line, int32_t number, uint8_t len) 
{  
	char   Number_Buffer[15];				// 用于存储转换后的字符串
	// if( LCD.ShowNum_Mode == Fill_Zero)	// 多余位补0
	if( my_lcd.show_num_mode ==0)	// 多余位补0
	{
		sprintf( Number_Buffer , "%0.*d",len, number );	// 将 number 转换成字符串，便于显示		
	}
	else			// 多余位补空格
	{	
		sprintf( Number_Buffer , "%*d",len, number );	// 将 number 转换成字符串，便于显示		
	}
	LCD_DisplayStringAt(0,  LINE(Line), (char *)Number_Buffer , LEFT_MODE) ;  // 将转换得到的字符串显示出来
}

/***************************************************************************************************************************************
*	函 数 名:	LCD_DisplayDecimals
*
*	入口参数:	x - 起始水平坐标，取值范围0~799 
*					y - 起始垂直坐标，取值范围0~479
*					decimals - 要显示的数字, double型取值1.7 x 10^（-308）~ 1.7 x 10^（+308），但是能确保准确的有效位数为15~16位
*
*       			len - 整个变量的总位数（包括小数点和负号），若实际的总位数超过了指定的总位数，将按实际的总长度位输出，
*							示例1：小数 -123.123 ，指定 len <=8 的话，则实际照常输出 -123.123
*							示例2：小数 -123.123 ，指定 len =10 的话，则实际输出   -123.123(负号前面会有两个空格位) 
*							示例3：小数 -123.123 ，指定 len =10 的话，当调用函数 LCD_ShowNumMode() 设置为填充0模式时，实际输出 -00123.123 
*
*					decs - 要保留的小数位数，若小数的实际位数超过了指定的小数位，则按指定的宽度四舍五入输出
*							 示例：1.12345 ，指定 decs 为4位的话，则输出结果为1.1235
*
*	函数功能:	在指定坐标显示指定的变量，包括小数
*
*	说    明:	1. 可设置要显示的字体，例如使用 LCD_SetTextFont(&CH_Font24) 设置为 2424的中文字体以及2412的ASCII字符字体
*					2.	可设置要显示的颜色，例如使用 LCD_SetColor(0xff0000FF) 设置为蓝色
*					3. 可设置对应的背景色，例如使用 LCD_SetBackColor(0xff000000) 设置为黑色的背景色
*					4. 使用示例 LCD_DisplayDecimals( 10, 10, a, 5, 3) ，在坐标(10,10)显示字变量a,总长度为5位，其中保留3位小数
*						
*****************************************************************************************************************************************/

void  LCD_DisplayDecimals( uint16_t Line, double decimals, uint8_t len, uint8_t decs) 
{  
	char  Number_Buffer[20];				// 用于存储转换后的字符串
	
	// if( LCD.ShowNum_Mode == Fill_Zero)	// 多余位填充0模式
	if( my_lcd.show_num_mode ==0)	// 多余位补0
	{
		sprintf( Number_Buffer , "%0*.*lf",len,decs, decimals );	// 将 number 转换成字符串，便于显示		
	}
	else		// 多余位填充空格
	{
		sprintf( Number_Buffer , "%*.*lf",len,decs, decimals );	// 将 number 转换成字符串，便于显示		
	}
	
	LCD_DisplayStringAt(0,   LINE(Line), (char *)Number_Buffer, LEFT_MODE) ;	// 将转换得到的字符串显示出来
}

/**
 * @brief  在显示器上显示一个中文字符
 * @param  usX ：在特定扫描方向下字符的起始X坐标
 * @param  usY ：在特定扫描方向下字符的起始Y坐标
 * @param  usChar ：要显示的中文字符（国标码）
 * @retval 无
 */ 
void LCD_DispChar_CH (uint16_t Xpos, uint16_t Ypos, uint16_t Text)
{
	uint32_t i = 0, j = 0;
	uint16_t height, width;
	uint8_t  offset;
	uint8_t  *pchar;
	// uint8_t  Buffer[HEIGHT_CH_CHAR*3];
	uint8_t  Buffer[HEIGHT_CH_CHAR*3];
	uint32_t line;
		
	GetGBKCode (Buffer, Text );  // TODO 获得Buffer
  
	// height = 	HEIGHT_CH_CHAR;//取字模数据//获取正在使用字体高度
	// width  =  WIDTH_CH_CHAR; //获取正在使用字体宽度
	height = my_lcd.draw_prop[my_lcd.activelayer].pCnFont->Height;//获取正在使用字体高度
	width  = my_lcd.draw_prop[my_lcd.activelayer].pCnFont->Width; //获取正在使用字体宽度
  
	offset =  8 *((width + 7)/8) -  width ;//计算字符的每一行像素的偏移值，实际存储大小-字体宽度
	
	for(i = 0; i < height; i++)//遍历字体高度绘点
	{
		pchar = ((uint8_t *)Buffer + (width + 7)/8 * i);//计算字符的每一行像素的偏移地址
		
		switch(((width + 7)/8))//根据字体宽度来提取不同字体的实际像素值
		{
		
		case 1:
		line =  pchar[0];      //提取字体宽度小于8的字符的像素值
		break;
		
		case 2:
		line =  (pchar[0]<< 8) | pchar[1]; //提取字体宽度大于8小于16的字符的像素值     
		break;
		
		case 3:
		default:
		line =  (pchar[0]<< 16) | (pchar[1]<< 8) | pchar[2]; //提取字体宽度大于16小于24的字符的像素值     
		break;
		} 
		
		for (j = 0; j < width; j++)//遍历字体宽度绘点
		{
		if(line & (1 << (width- j + offset- 1))) //根据每一行的像素值及偏移位置按照当前字体颜色进行绘点
		{
			LCD_Draw_Point((Xpos + j), Ypos, DrawProp[ActiveLayer].TextColor);
		}
		else//如果这一行没有字体像素则按照背景颜色绘点
		{
			LCD_Draw_Point((Xpos + j), Ypos, DrawProp[ActiveLayer].BackColor);
		} 
		}
		Ypos++;
	}
}

/**
  * @brief  显示一行字符，若超出液晶宽度，不自动换行。
			中英混显时，请把英文字体设置为Font24格式
  * @param  Line: 要显示的行编号LINE(0) - LINE(N)
  * @param  *ptr: 要显示的字符串指针
  * @retval None
  */
void LCD_DisplayStringLine_EN_CH(uint16_t Line, uint8_t *ptr)
{  
  uint16_t refcolumn = 0;
  /* 判断显示位置不能超出液晶的边界 */
  while ((refcolumn < LCD_PIXEL_WIDTH) && ((*ptr != 0) & (((refcolumn + DrawProp[ActiveLayer].pFont->Width) & 0xFFFF) >= DrawProp[ActiveLayer].pFont->Width)))
  {
	/* 使用LCD显示一个字符 */
	if ( * ptr <= 126 )	           	//英文字符
	{
				
		LCD_DisplayChar(refcolumn, LINE(Line), *ptr);
		/* 根据字体偏移显示的位置 */
		refcolumn += DrawProp[ActiveLayer].pFont->Width;
		/* 指向字符串中的下一个字符 */
		ptr++;
	}
	
	else	                            //汉字字符
	{	
		uint16_t usCh;
		
		/*一个汉字两字节*/
		usCh = * ( uint16_t * ) ptr;	
		/*交换编码顺序*/
		usCh = ( usCh << 8 ) + ( usCh >> 8 );		
		
		/*显示汉字*/
		LCD_DispChar_CH ( refcolumn,LINE(Line) , usCh );
		/*显示位置偏移*/
		refcolumn += WIDTH_CH_CHAR;
		/* 指向字符串中的下一个字符 */
		ptr += 2; 		
    }		
  }
}


void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
			yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
			curpixel = 0;

	deltax = ABS(x2 - x1); /* 求x轴的绝对值 */
	deltay = ABS(y2 - y1); /* 求y轴的绝对值 */
	x = x1;				   /* 第一个像素的x坐标起始值 */
	y = y1;				   /* 第一个像素的y坐标起始值 */

	if (x2 >= x1) /* x坐标值为递增 */
	{
		xinc1 = 1;
		xinc2 = 1;
	}
	else /* x坐标值为递减 */
	{
		xinc1 = -1;
		xinc2 = -1;
	}

	if (y2 >= y1) /* y坐标值为递增 */
	{
		yinc1 = 1;
		yinc2 = 1;
	}
	else /* y坐标值为递减 */
	{
		yinc1 = -1;
		yinc2 = -1;
	}

	if (deltax >= deltay) /* 每个 y 坐标值至少有一个x坐标值*/
	{
		xinc1 = 0; /* 当分子大于或等于分母时不要改变 x */
		yinc2 = 0; /* 不要为每次迭代更改 y */
		den = deltax;
		num = deltax / 2;
		num_add = deltay;
		num_pixels = deltax; /* x比y多的值 */
	}
	else /* 每个 x 坐标值至少有一个y坐标值 */
	{
		xinc2 = 0; /* 不要为每次迭代更改 x */
		yinc1 = 0; /* 当分子大于或等于分母时不要改变 y */
		den = deltay;
		num = deltay / 2;
		num_add = deltax;
		num_pixels = deltay; /* y比x多的值 */
	}

	for (curpixel = 0; curpixel <= num_pixels; curpixel++)
	{
		LCD_Draw_Point(x, y, DrawProp[my_ltdc.activelayer].TextColor); /* 绘制当前像素点 */
		num += num_add;												   /* 在分数的基础上增加分子 */
		if (num >= den)												   /* 检查分子大于或等于分母 */
		{
			num -= den; /* 计算新的分子值 */
			x += xinc1; /* x值递增 */
			y += yinc1; /* y值递增 */
		}
		x += xinc2; /* y值递增 */
		y += yinc2; /* y值递增 */
	}
}

/**
 * @brief  填充三角形（基于三点）
 * @param  x1: 第一点的X坐标值
 * @param  y1: 第一点的Y坐标值
 * @param  x2: 第二点的X坐标值
 * @param  y2: 第二点的Y坐标值
 * @param  x3: 第三点的X坐标值
 * @param  y3: 第三点的Y坐标值
 * @retval 无
 */
void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
{
	int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,
			yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
			curpixel = 0;

	deltax = ABS(x2 - x1); /* 求x轴的绝对值 */
	deltay = ABS(y2 - y1); /* 求y轴的绝对值 */
	x = x1;				   /* 第一个像素的x坐标起始值 */
	y = y1;				   /* 第一个像素的y坐标起始值 */

	if (x2 >= x1) /* x坐标值为递增*/
	{
		xinc1 = 1;
		xinc2 = 1;
	}
	else /* x坐标值为递减 */
	{
		xinc1 = -1;
		xinc2 = -1;
	}

	if (y2 >= y1) /* y坐标值为递增*/
	{
		yinc1 = 1;
		yinc2 = 1;
	}
	else /* y坐标值为递减 */
	{
		yinc1 = -1;
		yinc2 = -1;
	}

	if (deltax >= deltay) /* 每个 y 坐标值至少有一个x坐标值*/
	{
		xinc1 = 0; /* 当分子大于或等于分母时不要改变 x */
		yinc2 = 0; /* 不要为每次迭代更改 y */
		den = deltax;
		num = deltax / 2;
		num_add = deltay;
		num_pixels = deltax; /* x比y多的值 */
	}
	else /* 每个 x 坐标值至少有一个y坐标值 */
	{
		xinc2 = 0; /* 不要为每次迭代更改 x */
		yinc1 = 0; /* 当分子大于或等于分母时不要改变 y */
		den = deltay;
		num = deltay / 2;
		num_add = deltax;
		num_pixels = deltay; /* y比x多的值 */
	}

	for (curpixel = 0; curpixel <= num_pixels; curpixel++)
	{
		LCD_DrawLine(x, y, x3, y3);

		num += num_add; /* 在分数的基础上增加分子 */
		if (num >= den) /* 判断分子是否大于或等于分母 */
		{
			num -= den; /* 计算新的分子值 */
			x += xinc1; /* x值递增 */
			y += yinc1; /* y值递增 */
		}
		x += xinc2; /* x值递增 */
		y += yinc2; /* y值递增 */
	}
}

// 画矩形
//(x1,y1),(x2,y2):矩形的对角坐标
void LCD_DrawRectangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
	LCD_DrawLine(x1, y1, x2, y1);
	LCD_DrawLine(x1, y1, x1, y2);
	LCD_DrawLine(x1, y2, x2, y2);
	LCD_DrawLine(x2, y1, x2, y2);
}

// 在指定位置画一个指定大小的圆
//(x,y):中心点
// r    :半径
void LCD_DrawCircle(uint16_t x, uint16_t y, uint16_t r)
{
	int Xadd = -r, Yadd = 0, err = 2-2*r, e2;
	do {   

		LCD_Draw_Point(x-Xadd,y+Yadd,my_lcd.color);
		LCD_Draw_Point(x+Xadd,y+Yadd,my_lcd.color);
		LCD_Draw_Point(x+Xadd,y-Yadd,my_lcd.color);
		LCD_Draw_Point(x-Xadd,y-Yadd,my_lcd.color);
		
		e2 = err;
		if (e2 <= Yadd) {
			err += ++Yadd*2+1;
			if (-Xadd == Yadd && e2 <= Xadd) e2 = 0;
		}
		if (e2 > Xadd) err += ++Xadd*2+1;
    }
    while (Xadd <= 0);
    
}


// 在指定区域内填充单个颜色
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
// color:要填充的颜色
void LCD_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint32_t color)
{
	// TODO: 未测试正确
	uint32_t offline;
	uint32_t address;
	offline = my_lcd.width - (ex-sx+1);
	address = my_lcd.layeraddr[my_lcd.activelayer] + my_lcd.pixelsize*(sy*my_lcd.width +sx);

    // DMA2D->CR      = 0x00030000UL;                                  // 配置为寄存器到储存器模式
    // DMA2D->OCOLR   = color;                                         // 设置填充使用的颜色，格式应该与设置的颜色格式相同
    // DMA2D->OMAR    = (uint32_t)address;                                // 填充区域的起始内存地址
    // DMA2D->OOR     = offline;                                       // 行偏移，即跳过的像素，注意是以像素为单位
    // DMA2D->OPFCCR  = my_lcd.color_format;                                   // 设置颜色格式
    // DMA2D->NLR     = (uint32_t)( (ex-sx+1) << 16) | (uint16_t)(ey-sy+1);    // 设置填充区域的宽和高，单位是像素

    // // 传输中断
    // // DMA2D->CR |= DMA2D_IT_TC|DMA2D_IT_TE|DMA2D_IT_CE;
    // // DMA2D->CR   |= DMA2D_CR_START;

    // // 启动传输 阻塞  -- 阻塞一般用于清屏，R2M 模式
    // DMA2D->CR   |= DMA2D_CR_START;   
    // while (DMA2D->CR & DMA2D_CR_START) {} 

	DMA2D_Fill(address,  (ex-sx+1), (ey-sy+1), offline, my_lcd.color_format, color);
}

// 在指定区域内填充指定颜色块
//(sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex-sx+1)*(ey-sy+1)
// color:要填充的颜色
// 注意两个地址的offline
void LCD_Color_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color)
{
	// LTDC_Color_Fill(sx, sy, ex, ey, color);
	uint32_t offline;
	offline = my_lcd.width - (ex-sx+1);
	DMA2D_MemCopy(my_lcd.color_format, 
		color, (uint32_t)my_lcd.layeraddr[my_lcd.activelayer], 
		(ex-sx+1), (ey-sy+1), offline, offline );
}

void LCD_Clear( uint32_t color)
{
	LCD_Fill( 0, 0, my_lcd.width, my_lcd.height, color );
}

/***************************************************************************************************************************************
*	函 数 名: LCD_FillRect
*
*	入口参数: x - 水平坐标，取值范围 0~799
*			 	 y - 垂直坐标，取值范围 0~479
*			 	 width  - 图片的水平宽度，最大取值800
*				 height - 图片的垂直宽度，最大取值480
*
*	函数功能: 在坐标 (x,y) 填充指定长宽的实心矩形
*
*	说    明: 1. 使用DMA2D实现
*				 2. 要绘制的区域不能超过屏幕的显示区域
*						 
*****************************************************************************************************************************************/

void LCD_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height)
{
	DMA2D->CR	  &=	~(DMA2D_CR_START);				//	停止DMA2D
	DMA2D->CR		=	DMA2D_R2M;							//	寄存器到SDRAM
	DMA2D->OPFCCR	=	my_lcd.color_format;						//	设置颜色格式
	DMA2D->OCOLR	=	my_lcd.color;							//	颜色
	
	// if(my_lcd.dir == 1)  //横屏填充
	// {		
	// 	DMA2D->OOR		=	my_lcd.width - width;				//	设置行偏移 
	// 	DMA2D->OMAR		=	my_ltdc.layeraddr[my_ltdc.activelayer] + LCD.BytesPerPixel*(my_lcd.width * y + x);	// 地址;
	// 	DMA2D->NLR		=	(width<<16)|(height);			//	设定长度和宽度		
	// }
	// else	//竖屏填充
	// {		
	// 	DMA2D->OOR		=	my_lcd.width - height;		//	设置行偏移 
	// 	DMA2D->OMAR		=	my_ltdc.layeraddr[my_ltdc.activelayer] + LCD.BytesPerPixel*((my_lcd.height - x - 1 - width)*LCD_Width + y);	// 地址
	// 	DMA2D->NLR		=	(width)|(height<<16);	//	设定长度和宽度		
	// }		

	DMA2D->OOR		=	my_lcd.width - width;				//	设置行偏移 
	DMA2D->OMAR		=	my_lcd.layeraddr[my_lcd.activelayer] + my_lcd.pixelsize*(my_lcd.width * y + x);	// 地址;
	DMA2D->NLR		=	(width<<16)|(height);			//	设定长度和宽度	
	DMA2D->CR	  |=	DMA2D_CR_START;					//	启动DMA2D
		
	while (DMA2D->CR & DMA2D_CR_START) ;			//	等待传输完成
}

/***************************************************************************************************************************************
*	函 数 名: LCD_FillCircle
*
*	入口参数: x - 圆心 水平坐标，取值范围 0~799
*			 	 y - 圆心 垂直坐标，取值范围 0~479
*			 	 r  - 半径
*
*	函数功能: 在坐标 (x,y) 填充半径为 r 的圆形区域
*
*	说    明: 1. 该函数移植于ST官方评估板的例程
*				 2. 要绘制的区域不能超过屏幕的显示区域
*
*****************************************************************************************************************************************/

void LCD_FillCircle(uint16_t x, uint16_t y, uint16_t r)
{
	int32_t  D;    /* Decision Variable */ 
	uint32_t  CurX;/* Current X Value */
	uint32_t  CurY;/* Current Y Value */ 
	
	D = 3 - (r << 1);
	
	CurX = 0;
	CurY = r;
	
	while (CurX <= CurY)
	{
		if(CurY > 0) 
		{ 
			LCD_DrawLine(x - CurX, y - CurY,x - CurX,y - CurY + 2*CurY);
			LCD_DrawLine(x + CurX, y - CurY,x + CurX,y - CurY + 2*CurY); 
		}
		
		if(CurX > 0) 
		{
			LCD_DrawLine(x - CurY, y - CurX,x - CurY,y - CurX + 2*CurX);
			LCD_DrawLine(x + CurY, y - CurX,x + CurY,y - CurX + 2*CurX); 		 
		}
		if (D < 0)
		{ 
		D += (CurX << 2) + 6;
		}
		else
		{
		D += ((CurX - CurY) << 2) + 10;
		CurY--;
		}
		CurX++;
	}
	
	LCD_DrawCircle(x, y, r);  
}


/***************************************************************************************************************************************
*	函 数 名: LCD_DrawImage
*
*	入口参数: x - 水平坐标，取值范围 0~799
*			 	 y - 垂直坐标，取值范围 0~479
*			 	 width  - 图片的水平宽度，最大取值800
*				 height - 图片的垂直宽度，最大取值480
*				*pImage - 图片数据存储区的首地址
*
*	函数功能: 在指定坐标处显示图片
*
*	说    明: 要显示的图片需要事先进行取模，且只能显示一种颜色，使用 LCD_SetColor() 函数设置画笔色
*						 
*****************************************************************************************************************************************/

void 	LCD_DrawImage(uint16_t x,uint16_t y,uint16_t width,uint16_t height,const uint8_t *pImage) 
{  
   uint8_t   disChar;	//字模的值
	uint16_t  Xaddress = x; //水平坐标
	uint16_t  i=0,j=0,m=0;
	
	for(i = 0; i <height; i++)
	{
		for(j = 0; j <(float)width/8; j++)
		{
			disChar = *pImage;

			for(m = 0; m < 8; m++)
			{ 
				if(disChar & 0x01)	
				{		
					LCD_Draw_Point(Xaddress,y,my_lcd.color);	//当前模值不为0时，使用画笔色绘点
				}
				else		
				{		
					LCD_Draw_Point(Xaddress,y,my_lcd.color);	//否则使用背景色绘制点
				}
				disChar >>= 1;
				Xaddress++;  //水平坐标自加
				
				if( (Xaddress - x)==width ) //如果水平坐标达到了字符宽度，则退出当前循环
				{													//进入下一行的绘制
					Xaddress = x;
					y++;
					break;
				}
			}	
			pImage++;			
		}
	}	
}