17.render/render.c文件分析

本文深入探讨嵌入式系统中GUI渲染的核心机制,包括字符和图片的显示、内存管理和优化技巧。解析了FlushVideoMemToDev、GetPixelDatasForIcon、MergeOneFontToVideoMem等关键函数的功能与实现细节,以及它们如何协同工作以提高渲染效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个文件主要是写了一些图片、文字的渲染,主要调用fonts、encoding、picfrm这几个文件中的函数

所有函数的简单分析

/* PT_VideoMem预先分配好的内存,刷到fb上,最终调用的是memcpy,在fb.c中,提供的一个ShowPage函数 */
void FlushVideoMemToDev(PT_VideoMem ptVideoMem)

/* 查看某一个文件是否支持bmp或者jpg格式,0不支持,1支持*/
int isPictureFileSupported(char *strFileName)
/**************************************************************************************************/
 /* 获取ICON_PATH地址下,获取bmp格式的图标的数据,这里注意是bmp格式
  * 注意这里传进来的ptPixelDatas,是个指针,其是在上一层函数中分配的
  * 这个函数里未分配内存,而又写了一个和这个函数相似的函数来释放内存,写的有点乱
  */
int GetPixelDatasForIcon(char *strFileName, PT_PixelDatas ptPixelDatas)
/* 这个名字改成 FreePixelDatas 比较好,毕竟就是释放一个T_PixelDatas结构体
 * 并不在乎是不是图标的PT_PixelDatas
 * 而名字这样写,搞的像和上面那个函数对应似的,好像内存是在上面的函数中分配的
 * free(ptPixelDatas->aucPixelDatas); 最终也是调用的这个函数,不懂为啥要写这么多,不会搞混么
 */
void FreePixelDatasForIcon(PT_PixelDatas ptPixelDatas)
/**************************************************************************************************/
/* 从一个文件中获取像素数据,入参和GetPixelDatasForIcon一毛子一样
  * GetPixelDatasForIcon函数是单单获得BMP文件的数据
  * 这个函数是找一个可以使用的解释器获得数据,不明白有这个了,为啥还要上面那个函数
  */
int GetPixelDatasFrmFile(char *strFileName, PT_PixelDatas ptPixelDatas)
/* 不懂这里为啥又有一个 */
void FreePixelDatasFrmFile(PT_PixelDatas ptPixelDatas)
/**************************************************************************************************/
/* ptVideoMem这块内存中的(x,y)点设置成dwColor颜色 */
static int SetColorForPixelInVideoMem(int iX, int iY, PT_VideoMem ptVideoMem, unsigned int dwColor)

/* 把ptVideoMem中 从(iTopLeftX,iTopLeftY)到(iBotRightX,iBotRighY)这个范围内设置成颜色dwColor */
void ClearRectangleInVideoMem(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, PT_VideoMem ptVideoMem, unsigned int dwColor)

/* 判断ptFontBitMap里的字符是不是在指定的区域内 */
static int isFontInArea(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, PT_FontBitMap ptFontBitMap)

/* 这两个函数其实就是调用了一下InvertButton 把数据反转了一下*/
void PressButton(PT_Layout ptLayout)
void ReleaseButton(PT_Layout ptLayout)

三个关键函数详解

MergeOneFontToVideoMem函数

/* 把一个字符,显示到ptVideoMem里,字符里有位置的信息 */
static int MergeOneFontToVideoMem(PT_FontBitMap ptFontBitMap, PT_VideoMem ptVideoMem)
{
	int i;
	int x, y;
	int bit;
	int iNum;
	unsigned char ucByte;

	if (ptFontBitMap->iBpp == 1)	/* 如果字体数据里面,一个像素用一位来表示,即一个字节就表示了8个像素*/
	{
		for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)	/* 一行一行的 */
		{
			/* 第 y - ptFontBitMap->iYTop + 1 行数据距离原点的偏移量 
			 * 比如,这个"字"的第1行像素的起始位置(这个就是字符数据的起始位置),是 0    * ptFontBitMap->iPitch = 0 
		     * 比如,这个"字"的第4行像素的起始位置,距离字符数据原点的偏移量是: 3 * ptFontBitMap->iPitch
		     * ptFontBitMap->iPitch也有说明,是第N行到第N+1行数据之间的偏移量,就是一行像素占用多少字节
		     */
			i = (y - ptFontBitMap->iYTop) * ptFontBitMap->iPitch;	

			/* 一行里,x的坐标从最左边移动到最右边,一个像素一个像素的画,而8个像素,使用字符数据中的一个字节 */
			for (x = ptFontBitMap->iXLeft, bit = 7; x < ptFontBitMap->iXMax; x++)
			{
				/* bit==7 时表示字符数据中已经取完1个字节的数据了(也就是说已经用完8位,即描画了8个像素点了)
				 * 这里要将字符数据中下一个字节取出来,描画下一次的 8个像素
				 */
				if (bit == 7)
				{
					ucByte = ptFontBitMap->pucBuffer[i++];
				}

				/* 从最高位开始取,一次取1位,如果是1的话,就代表是需要画出的一个点*/
				/* 从这里也可以看出来,字符数据的一个字符,高位在左边,地位在右边*/
				if (ucByte & (1<<bit))
				{	/* 这里就给这个像素点上褐色的点,注意这里传进去的数据是32位 */
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, COLOR_FOREGROUND);
				}
				else
				{
					/* 如果不是1,就去这个坐标把背景色打上去,也就是说不画点 */
					// g_ptDispOpr->ShowPixel(x, y, 0); /* 黑 */
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, COLOR_BACKGROUND);
				}
				/* 在描点的时候出错了 */
				if (iNum == -1)
				{
					return -1;
				}
				/* 进入下一个字节 */
				bit--;
				/* 7.6.5.4.3.2.1.0 刚好是执行8次,即一个字节*/
				if (bit == -1)
				{
					bit = 7;
				}
			}
		}
	}
	
	/* 如果字符数据是8bpp的,即一个字符表示一个像素点 */
	else if (ptFontBitMap->iBpp == 8)		
	{
		/* 如上,从上到下,从左到右一行一行的来 */
		for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
			for (x = ptFontBitMap->iXLeft; x < ptFontBitMap->iXMax; x++)
			{
				//g_ptDispOpr->ShowPixel(x, y, ptFontBitMap->pucBuffer[i++]);
				/* 只有在这一个字节的数据==0时,才不需要画点 */
				if (ptFontBitMap->pucBuffer[i++])
				{
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, COLOR_FOREGROUND);
				}
				else
				{
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, COLOR_BACKGROUND);
				}
				
				if (iNum == -1)
				{
					return -1;
				}
			}
	}
	/* 只支持字体数据 1bpp 和 8bpp 的*/
	else
	{
		DBG_PRINTF("ShowOneFont error, can't support %d bpp\n", ptFontBitMap->iBpp);
		return -1;
	}
	return 0;
}

MergerStringToCenterOfRectangleInVideoMem函数

/* 把一个字符串pucTextString,显示到ptVideoMem内存中的 (iTopLeftX,iTopLeftY) (iBotRightX,iBotRightY)这个区域内 */
int MergerStringToCenterOfRectangleInVideoMem(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, unsigned char *pucTextString, PT_VideoMem ptVideoMem)
{
	int iLen;
	int iError;
	unsigned char *pucBufStart;
	unsigned char *pucBufEnd;
	unsigned int dwCode;
	T_FontBitMap tFontBitMap;	/* 存放字符串中一个字符的信息 */
	
	int bHasGetCode = 0;

	int iMinX = 32000, iMaxX = -1;
	int iMinY = 32000, iMaxY = -1;

	int iStrTopLeftX , iStrTopLeftY;	/* 字符串左上角的坐标 */

	int iWidth, iHeight;

	/* 先设置第一个字的原点为(0,0)
	 * 这里有一个问题先存疑,(iCurOriginX,iCurOriginY)是以左下角为坐标原点
	 * 而这里都设置成了(0,0),那么在freetype.c中计算的左上角坐标就不是正整数了
	 * 这里没问题,可以这么写,最大和最小的Y坐标都是负数,大减小就是正数了
	 */
	tFontBitMap.iCurOriginX = 0;
	tFontBitMap.iCurOriginY = 0;
	/* 字符串开始的地址 */
	pucBufStart = pucTextString;
	/* 字符串结束的地址,*/
	pucBufEnd   = pucTextString + strlen((char *)pucTextString);

	/* 清除这个区域 */
	ClearRectangleInVideoMem(iTopLeftX, iTopLeftY, iBotRightX, iBotRightY, ptVideoMem, COLOR_BACKGROUND);
	
	/* 先计算字符串显示的总体宽度、高度 */
	while (1)
	{
		/* 使用ASCII或者GBK,取出字符的编码
		 * 如果字符是英文,那么就使用一个字节,返回1,并把其编码放到dwCode中
		 * 如果字符是中文,那么就用两个字节,返回2,并把编码放到dwCode中
		 */
		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
		if (0 == iLen)
		{
			/* 就是没有获得编码,要么是字符串完了,要么是这个字符串里没有字符 */
			/* 如果bHasGetCode在这里时等于0,那就代表这个地址指向的地方没有字符串,直接返回错误*/
			if (!bHasGetCode)
			{
				//DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
				return -1;
			}
			else
			{
				break;
			}
		}
		
		bHasGetCode = 1;
		/* 此时,pucBufStart指向了下一个字符 */
		pucBufStart += iLen;

		/* 获得字符的位图, 位图信息里含有字符显示时的左上角、右下角坐标,在freetype.c中的函数里设置 */
		iError = GetFontBitmap(dwCode, &tFontBitMap);
		if (0 == iError)
		{	
			/* 在这里比较,获得最大的框 */
			/* 这iMinX是在第一个字符中得到的 */
			if (iMinX > tFontBitMap.iXLeft)
			{
				iMinX = tFontBitMap.iXLeft;
			}
			/* iMaxY是在最后一个字符中得到的 */
			if (iMaxX < tFontBitMap.iXMax)
			{
				iMaxX = tFontBitMap.iXMax;
			}

			/* 关于Y坐标,是可能在任何一个字符中得到的,因为每个字符的高度都不确定,不一定那个字符高哪个字符低
			 * 这里面都是负值,因为一开始把左下角的原点定在了(0,0),freetype.c中是从左下角往上算的
			 * 但是对于后面是没有影响的
			 */
			if (iMinY > tFontBitMap.iYTop)
			{
				iMinY = tFontBitMap.iYTop;
			}
			/* 源码写错了 iMaxY < tFontBitMap.iXMax */
			if (iMaxY < tFontBitMap.iYMax)
			{
				iMaxY = tFontBitMap.iYMax;
			}

			/* 前一个字符里的“下个字符的原点”,就是下一个字符用的原点 */
			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
		}
		else
		{
			DBG_PRINTF("GetFontBitmap for calc width/height error!\n");
		}
	}	
	/* 算出整个字符串所占用的长宽,这里iMaxY、iMinY都是负数,可能iMaxY是0,而两个一减就都是正数了,所以没问题 */
	iWidth  = iMaxX - iMinX;
	iHeight = iMaxY - iMinY;

    /* 如果字符串过长,那么就显示一部分 */
    if (iWidth > iBotRightX - iTopLeftX)
    {
        iWidth = iBotRightX - iTopLeftX;
    }

    /* 如果字符串过高,就不显示了*/
	if (iHeight > iBotRightY - iTopLeftY)
	{
		DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
		//DBG_PRINTF("iHeight = %d, iBotRightY - iTopLeftX = %d - %d = %d\n", iHeight, iBotRightY, iTopLeftY, iBotRightY - iTopLeftY);
		return -1;
	}
	//DBG_PRINTF("iWidth = %d, iHeight = %d\n", iWidth, iHeight);

	/* 2.确定第1个字符的原点 
	 * 2.1 先计算左上角坐标
	 */
	iStrTopLeftX = iTopLeftX + (iBotRightX - iTopLeftX - iWidth) / 2;
	iStrTopLeftY = iTopLeftY + (iBotRightY - iTopLeftY - iHeight) / 2;


	/* 2.2 再计算第1个字符原点坐标,左下角的坐标
	 * iMinX - 原来的iCurOriginX(0) = iStrTopLeftX - 新的iCurOriginX
	 * iMinY - 原来的iCurOriginY(0) = iStrTopLeftY - 新的iCurOriginY
	 */
	/* 这里减iMinX是因为,当原点是(0,0)时,字符串最左边是在iMinX
	 * 而如果把字符串的最左边设置成iStrTopLeftX时,那么字符串的的最左边是在iStrTopLeftX+iMinX这个地方了,就相当于字符串右移了
	 * 那就不是我们想让它出现的位置了,所以这里提前减去iMinX,作为左下角原点X坐标
	 */
	tFontBitMap.iCurOriginX = iStrTopLeftX - iMinX;
	/* 首先,iMinY在上面来说是个负数,这里减去一个负数,相当于加一个数
	 * iStrTopLeftY是左上角的坐标,需要加上一个值编程左下角的坐标
	 * 这里为什么加 |iMinY| ,而不加上面算出的高度iHeight,我认为原因和上面算横坐标是一样的
	 * freetype给出的字体像素数据,是以左下角为原点来说的,但并不是说其给的元素像素都是在第一象限,即xy坐标都是正的
	 * 像比如字母g,它的y坐标有负值
	 * 所以如果这里直接加一个高度iHeight,那么其真实的高度又会超出iStrTopLeftY+iHeight,就超出了我们设想的那个框
	 */
	tFontBitMap.iCurOriginY = iStrTopLeftY - iMinY;	
	
	pucBufStart = pucTextString;	
	bHasGetCode = 0;
	while (1)
	{
		/* 从字符串中逐个取出字符 */
		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
		if (0 == iLen)
		{
			/* 字符串结束 
		     * 其实这个地方bHasGetCode 已经不需要判断了,能执行到这说明字符串肯定不是空的 */
			if (!bHasGetCode)
			{
				DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
				return -1;
			}
			else
			{
				break;
			}
		}
		/* 执行这个while之前,没有bHasGetCode=0,所以这一句也是没有意义的 */
		bHasGetCode = 1;
		pucBufStart += iLen;

		/* 获得字符的位图 
		 * 由左下角原点,算出其他点的位置,比如左上角的原点,bpp,pitch,iXmax,iYmax等,都是在这个函数里算的
		 * 调用这个函数之前只需要算出左下角的原点就行
		 */
		iError = GetFontBitmap(dwCode, &tFontBitMap);
		if (0 == iError)
		{
			//DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
			/* 显示一个字符 */
            if (isFontInArea(iTopLeftX, iTopLeftY, iBotRightX, iBotRightY, &tFontBitMap))
            {
    			if (MergeOneFontToVideoMem(&tFontBitMap, ptVideoMem))
    			{
    				DBG_PRINTF("MergeOneFontToVideoMem error for code 0x%x\n", dwCode);
    				return -1;
    			}
            }
            else
            {
                return 0;
            }
			/* 这个结构体把原点设置成下一个字符的原点 */
			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
		}
		else
		{
			DBG_PRINTF("GetFontBitmap for drawing error!\n");
		}
	}

	return 0;
}

InvertButton函数

/* 把某一块图层的数据全部取反,ptLayout携带着需要取反的那一块空间的xy坐标 */
static void InvertButton(PT_Layout ptLayout)
{
	int iY;
	int i;
	int iButtonWidthBytes;
	unsigned char *pucVideoMem;
	/* 获得默认的显示设备 */
	PT_DispOpr ptDispOpr = GetDefaultDispDev();
	/* 获得默认显示设备的显存 */
	pucVideoMem = ptDispOpr->pucDispMem;
	/* 算出需要取反的空间的起始地址 */
	pucVideoMem += ptLayout->iTopLeftY * ptDispOpr->iLineWidth + ptLayout->iTopLeftX * ptDispOpr->iBpp / 8; /* 图标在Framebuffer中的地址 */
	/* 一行需要给多少字节的数据取反 */
	iButtonWidthBytes = (ptLayout->iBotRightX - ptLayout->iTopLeftX + 1) * ptDispOpr->iBpp / 8;

	/* 一行一行的来 */
	for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++)
	{
		/* 每一行中,一个字节一个字节的来 */
		for (i = 0; i < iButtonWidthBytes; i++)
		{
			pucVideoMem[i] = ~pucVideoMem[i];  /* 取反 */
		}
		/* 取完一行,开始下一行 */
		pucVideoMem += ptDispOpr->iLineWidth;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值