首页 > 其他分享 >数码相框-测试&项目效果&部分代码讲解

数码相框-测试&项目效果&部分代码讲解

时间:2024-04-13 19:34:18浏览次数:28  
标签:函数 iError int 相框 数码 讲解 atLayout 输入 图标

测试

在makefile加上这句话意味着把警告当成错误处理:

image

这里看视频跟着来就好了。

input_manager.c详解


#include <config.h>
#include <input_manager.h>
#include <string.h>

static PT_InputOpr g_ptInputOprHead;
static T_InputEvent g_tInputEvent;

static pthread_mutex_t g_tMutex  = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  g_tConVar = PTHREAD_COND_INITIALIZER;

/**
 * 注册一个输入操作
 * @param ptInputOpr 指向输入操作结构体的指针
 * @return 总是返回0
 */

int RegisterInputOpr(PT_InputOpr ptInputOpr)
{
    PT_InputOpr ptTmp;

    // 如果链表为空,将传入的操作作为头结点
    if (!g_ptInputOprHead)
    {
        g_ptInputOprHead   = ptInputOpr;
        ptInputOpr->ptNext = NULL;
    }
    else
    {
        // 遍历链表,找到最后一个节点
        ptTmp = g_ptInputOprHead;
        while (ptTmp->ptNext)
        {
            ptTmp = ptTmp->ptNext;
        }
        // 将新操作添加到链表末尾
        ptTmp->ptNext	  = ptInputOpr;
        ptInputOpr->ptNext = NULL;
    }

    return 0;
}

/**
 * 函数名称:ShowInputOpr
 * 功能描述:遍历并显示输入操作的列表。
 * 参数列表:无
 * 返回值:无
 */

void ShowInputOpr(void)
{
    int i = 0; // 用于编号的计数器
    PT_InputOpr ptTmp = g_ptInputOprHead; // 指向输入操作链表头部的指针

    while (ptTmp) // 遍历链表
    {
        printf("%02d %s\n", i++, ptTmp->name); // 打印操作的编号和名称
        ptTmp = ptTmp->ptNext; // 指向下一个输入操作
    }
}
/**
 * @brief 输入事件线程函数
 * 
 * 该函数为一个持续运行的线程,其主要功能是从外部获取输入事件,然后将该事件通过条件变量通知给主线程。
 * 使用互斥锁来保护对全局输入事件变量的访问,以确保线程安全。
 * 
 * @param pVoid 指向函数的指针,该函数用于获取输入事件。
 * @return void* 总是返回NULL,因为线程设计为长期运行,不会主动退出。
 */
static void *InputEventTreadFunction(void *pVoid)
{
    T_InputEvent tInputEvent;
  
    /* 将传入的void指针转换为获取输入事件的函数指针 */
    int (*GetInputEvent)(PT_InputEvent ptInputEvent);
    GetInputEvent = (int (*)(PT_InputEvent))pVoid;

    while (1)
    {
        /* 调用函数获取输入事件,如果获取成功,则通知主线程 */
        if(0 == GetInputEvent(&tInputEvent))
        {
            /* 在访问共享资源之前锁定互斥量,以确保线程安全 */
            pthread_mutex_lock(&g_tMutex);
            g_tInputEvent = tInputEvent; // 更新全局输入事件变量

            /* 发送信号以唤醒等待该事件的主线程 */
            pthread_cond_signal(&g_tConVar);

            /* 获取事件后释放互斥量 */
            pthread_mutex_unlock(&g_tMutex);
        }
    }

    return NULL;
}

/**
 * 初始化所有输入设备
 * 
 * 此函数遍历输入设备链表,对每个设备调用其初始化函数。如果设备初始化成功,
 * 则为该设备创建一个事件处理线程。
 * 
 * @return 成功初始化所有设备返回0,否则返回-1。
 */
int AllInputDevicesInit(void)
{
    PT_InputOpr ptTmp = g_ptInputOprHead; // 指向输入操作头部的指针
    int iError = -1; // 初始化错误码

    while (ptTmp)
    {
        // 尝试初始化设备
        if (0 == ptTmp->DeviceInit())
        {
            /* 创建设备的事件处理线程 */
			/* 创建子线程 */
            pthread_create(&ptTmp->tTreadID, NULL, InputEventTreadFunction, ptTmp->GetInputEvent);          
            iError = 0; // 设备初始化成功,错误码设为0
        }
        ptTmp = ptTmp->ptNext; // 指向下一个输入设备
    }
    return iError; // 返回错误码
}

/**
 * 获取输入事件
 * 
 * 本函数用于等待并获取输入事件。当有输入事件发生时,其他线程会唤醒等待中的函数实例。
 * 
 * @param ptInputEvent 指向输入事件结构体的指针,函数将在这里返回接收到的输入事件。
 * @return 总是返回0,表示操作成功。
 */
int GetInputEvent(PT_InputEvent ptInputEvent)
{
    /* 锁定互斥体以保护共享资源 */
	/* 休眠 */
    pthread_mutex_lock(&g_tMutex);
    /* 在条件变量上等待,直到被其他线程唤醒 */
    pthread_cond_wait(&g_tConVar, &g_tMutex);

	/* 被唤醒后,返回数据 */
    /* 唤醒后,将接收到的输入事件赋值给传入的指针 */
    *ptInputEvent = g_tInputEvent;
    /* 解锁互斥体 */
    pthread_mutex_unlock(&g_tMutex);
    return 0;
}
/**
 * 初始化输入设备
 * 本函数用于初始化标准输入设备(如键盘)和触摸屏等输入设备。
 * 
 * @param 无
 * @return 返回初始化过程中的错误码。如果初始化过程中有任何一步失败,都会返回相应的错误码。
 */
int InputInit(void)
{
	int iError; // 用于存储初始化过程中的错误码

	// 初始化标准输入设备
	iError = StdinInit();

	// 初始化触摸屏设备,并将之前的错误码与触摸屏初始化的错误码进行或运算,合并错误信息
	iError |= TouchScreenInit();

	return iError; // 返回错误码
}

这段C代码实现了一个输入事件管理系统,主要包括以下功能:

  1. 输入操作注册与显示

    • RegisterInputOpr()​ 函数用于注册一个新的输入操作到全局链表 g_ptInputOprHead​ 中。
    • ShowInputOpr()​ 函数遍历并打印已注册的所有输入操作的编号和名称。
  2. 输入事件线程与同步机制

    • 定义了全局变量 g_tInputEvent​ 存储当前输入事件,以及互斥锁 g_tMutex​ 和条件变量 g_tConVar​ 用于线程间同步。
    • InputEventTreadFunction()​ 是一个输入事件处理线程,它负责从外部获取输入事件,并通过条件变量通知主线程。互斥锁用于保护对全局输入事件变量的访问。
    • GetInputEvent()​ 函数等待并获取输入事件。当有事件发生时,它会被输入事件线程唤醒,并从全局变量中获取输入事件。
  3. 输入设备初始化与事件处理

    • AllInputDevicesInit()​ 函数遍历输入操作链表,对每个设备调用其 DeviceInit()​ 函数进行初始化。若初始化成功,为该设备创建一个输入事件处理线程(调用 InputEventTreadFunction()​)。
    • InputInit()​ 函数负责初始化标准输入设备(如键盘)和触摸屏等输入设备,并返回初始化过程中的错误码。

整体来看,这段代码构建了一个支持多输入设备、多输入操作的输入事件管理系统。通过注册输入操作,可以将不同类型的输入事件关联到相应的处理逻辑。系统使用线程和同步机制确保输入事件的高效、安全地传递与处理。最后,通过初始化函数设置好各个输入设备,使得整个系统能够正常运行。

选择并初始化默认设备

/**
 * 选择并初始化默认显示设备
 * @param name 指向要选择的显示设备名称的字符指针。
 * 注意:该函数不返回任何值。
 */
void SelectAndInitDefaultDispDev(char *name)
{
    // 根据提供的设备名称获取显示操作句柄
    g_ptDefaultDispOpr = GetDispOpr(name);
    if (g_ptDefaultDispOpr)
    {
        // 如果找到设备,则初始化设备
        g_ptDefaultDispOpr->DeviceInit();
        // 清除屏幕,准备显示
        g_ptDefaultDispOpr->CleanScreen(0);
    }
}

该函数用于选择并初始化默认的显示设备。函数的参数是指向要选择的显示设备名称的字符指针。函数内部根据提供的设备名称获取显示操作句柄,如果找到该设备,则对其进行初始化,并清除屏幕准备显示。

在开发板上测试

安装触摸屏驱动,设置环境变量:

image

新建图标目录,放入图标:

image

image

运行程序:

image

修改按钮按下的取反函数

像素跨越多个字节的情况

通常情况下,一个像素的颜色由若干个字节(通常为1至4个字节,取决于色彩深度)来表示。这里的 iBpp​ 表示每像素位数(bits per pixel),即每个像素所占的位数。例如:

  • 对于单色或黑白图像,iBpp​ 可能是 1,每个像素由 1 个位表示。
  • 对于常见的 24 位真彩色图像,iBpp​ 为 24,每个像素由 3 个字节(红、绿、蓝分量各占 8 位)表示。
  • 对于 32 位真彩色带 Alpha 通道的图像,iBpp​ 为 32,每个像素由 4 个字节表示(红、绿、蓝分量各占 8 位,Alpha 通道占 8 位)。

由于没有正确处理像素与字节之间的关系(例如,当 iBpp​ 不等于 8 时,一个像素可能跨越多个字节),直接以像素为单位更新索引可能会导致访问内存时跨过像素边界,从而影响颜色取反的效果,所以需要修改按钮按下的取反函数:


/*
 * 功能:翻转指定按钮区域的颜色
 * 参数:
 *      ptLayout - 指向按钮布局信息的指针
 * 返回值:无
 */

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) * ptDispOpr->iBpp / 8; /* 计算按钮宽度需要的字节 */

    /* 遍历按钮的每个像素行 */
    for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++)
    {
        /* 遍历当前行的每个像素 */
        for (i = 0; i < iButtonWidthBytes; i++)
        {
            pucVideoMem[i] = ~pucVideoMem[i];  /* 对当前像素的颜色取反 */
        }
        pucVideoMem += ptDispOpr->iLineWidth; /* 移动到下一行的起始位置 */
    }
}

项目效果演示

效果演示

效果演示:

image

动画

屏幕校准:

image

编译使用最终版的数码相框​

​​image

image

​​image​​

​​​image​​​

编译移植tslib:

韦东山 数码相框 项目学习(六)tslib的移植_tslib 源码包-CSDN博客

如果内核比较新,需要修改头文件,不然用不了:

image

驱动程序需要看这一节内容:

image

完整代码讲解

修改main_page

image

模块介绍

debug模块:

int DebugInit(void)
{
	int iError;

	iError = StdoutInit();
	iError |= NetPrintInit();
	return iError;
}

通过调用StdoutInit​和NetPrintInit​两个函数来初始化不同的调试输出通道,可以视为对特定功能(调试输出的初始化)进行了封装。这两个函数内部可能各自负责设置和配置其对应的输出机制(标准输出或网络打印),对外提供统一的接口,隐藏了实现细节。

image

display模块:

image

input模块:

image

要使用标准输入,必须要位于要程序位于前台。

font模块:

image

数码相框并没有用到asci i和gbk。

render模块:

image

page模块:

image

file模块:

image

encoding模块:

image

流程介绍

  1. 初始化模块数据:

    	/* 注册调试通道 */
    	DebugInit();
    
    	/* 初始化调试通道 */
    	InitDebugChanel();
    
    	if (argc != 2)
    	{
    		DBG_PRINTF("Usage:\n");
    		DBG_PRINTF("%s <freetype_file>\n", argv[0]);
    		return 0;
    	}
    
    	/* 注册显示设备 */
    	DisplayInit();
    	/* 可能可支持多个显示设备: 选择和初始化指定的显示设备 */
    	SelectAndInitDefaultDispDev("fb");
    
    	/* 
    	 * VideoMem: 为加快显示速度,我们事先在内存中构造好显示的页面的数据,
    	             (这个内存称为VideoMem)
    	 *           显示时再把VideoMem中的数据复制到设备的显存上
    	 * 参数的含义就是分配的多少个VideoMem
    	 * 参数可取为0, 这意味着所有的显示数据都是在显示时再现场生成,然后写入显存
    	 */
    	AllocVideoMem(5);
    
        /* 注册输入设备 */
    	InputInit();
        /* 调用所有输入设备的初始化函数 */
    	AllInputDevicesInit();
    
        /* 注册编码模块 */
        EncodingInit();
    
        /* 注册字库模块 */
    	iError = FontsInit();
    	if (iError)
    	{
    		DBG_PRINTF("FontsInit error!\n");
    	}
    
        /* 设置freetype字库所用的文件和字体尺寸 */
    	iError = SetFontsDetail("freetype", argv[1], 24);
    	if (iError)
    	{
    		DBG_PRINTF("SetFontsDetail error!\n");
    	}
    
        /* 注册图片文件解析模块 */
        PicFmtsInit();
    
        /* 注册页面 */
    	PagesInit();
    
  2. "主页面"的运行函数:

    static void MainPageRun(PT_PageParams ptParentPageParams)
    {
    	int iIndex;
    	T_InputEvent tInputEvent;
    	int bPressed = 0;
    	int iIndexPressed = -1;
        T_PageParams tPageParams;
    
        tPageParams.iPageID = ID("main");
    
    	/* 1. 显示页面 */
    	ShowMainPage(&g_tMainPageLayout);
    
    	/* 2. 创建Prepare线程 */
    
    	/* 3. 调用GetInputEvent获得输入事件,进而处理 */
    	while (1)
    	{
    		iIndex = MainPageGetInputEvent(&g_tMainPageLayout, &tInputEvent);
    		if (tInputEvent.iPressure == 0)
    		{
    			/* 如果是松开 */
    			if (bPressed)
    			{
    				/* 曾经有按钮被按下 */
    				ReleaseButton(&g_atMainPageIconsLayout[iIndexPressed]);
    				bPressed = 0;
    
    				if (iIndexPressed == iIndex) /* 按下和松开都是同一个按钮 */
    				{
    					switch (iIndexPressed)
    					{
    						case 0: /* 浏览按钮 */
    						{
    							Page("browse")->Run(&tPageParams);
    
    							/* 从设置页面返回后显示当首的主页面 */
    							ShowMainPage(&g_tMainPageLayout);
    
    							break;
    						}
    						case 1: /* 连播按钮 */
    						{
    							Page("auto")->Run(&tPageParams);
    
    							/* 从设置页面返回后显示当首的主页面 */
    							ShowMainPage(&g_tMainPageLayout);
    
    							break;
    						}
    						case 2: /* 设置按钮 */
    						{
    							Page("setting")->Run(&tPageParams);
    
    							/* 从设置页面返回后显示当首的主页面 */
    							ShowMainPage(&g_tMainPageLayout);
    
    							break;
    						}
    						default:
    						{
    							break;
    						}
    					}
    				}
    		
    				iIndexPressed = -1;
    			}
    		}
    		else
    		{
    			/* 按下状态 */
    			if (iIndex != -1)
    			{
    				if (!bPressed)
    				{
    					/* 未曾按下按钮 */
    					bPressed = 1;
    					iIndexPressed = iIndex;
    					PressButton(&g_atMainPageIconsLayout[iIndexPressed]);
    				}
    			}
    		}
    	}
    }
    

    该函数是一个“主页面”的运行函数,主要实现了以下功能:

    1. 在页面上显示菜单图标。
    2. 根据用户输入事件进行相应处理,包括按下和松开按钮等。
    3. 根据用户选择,调用相应的功能页面并执行相应的操作,如浏览、连播和设置等。
    4. 在从设置页面返回后,显示当前的主页面。

    该函数通过循环获取输入事件,并根据事件类型进行相应的处理。在按下按钮时,记录按下按钮的索引,并在松开按钮时,根据按下和松开的按钮是否相同来执行相应的操作。如果相同,则根据按钮索引调用相应的功能页面,并在返回后显示主页面。

  3. 显示页面:

     /* 运行主页面 */
    Page("main")->Run(NULL);
    

    显示主页面:

    /* 1. 显示页面 */
    ShowMainPage(&g_tMainPageLayout);
    
    static void ShowMainPage(PT_PageLayout ptPageLayout)
    {
    	PT_VideoMem ptVideoMem;
    	int iError;
    
    	PT_Layout atLayout = ptPageLayout->atLayout;
    
    	/* 1. 获得显存 */
    	ptVideoMem = GetVideoMem(ID("main"), 1);
    	if (ptVideoMem == NULL)
    	{
    		DBG_PRINTF("can't get video mem for main page!\n");
    		return;
    	}
    
    	/* 2. 描画数据 */
    
    	/* 如果还没有计算过各图标的坐标 */
    	if (atLayout[0].iTopLeftX == 0)
    	{
    		CalcMainPageLayout(ptPageLayout);
    	}
    
    	iError = GeneratePage(ptPageLayout, ptVideoMem);
    
    	/* 3. 刷到设备上去 */
    	FlushVideoMemToDev(ptVideoMem);
    
    	/* 4. 解放显存 */
    	PutVideoMem(ptVideoMem);
    }
    
    

    该函数的功能是显示一个名为"主页面"的界面。函数通过传入的ptPageLayout​​​​​参数获取页面布局信息,其中包括多个图标的文件名和显示区域。函数主要执行以下操作:

    1. 调用GetVideoMem​​​​​函数获取显示主页面所需的显存。
    2. 若获取显存失败,则打印错误信息并返回。
    3. 调用CalcMainPageLayout​​​​​函数计算各图标的坐标(如果之前未计算过)。
    4. 调用GeneratePage​​​​​函数根据页面布局信息和显存生成页面。
    5. 调用FlushVideoMemToDev​​​函数将生成的页面刷到设备上去。
    6. 调用PutVideoMem​​​​函数释放显存。
  4. 生成页面函数:

    int GeneratePage(PT_PageLayout ptPageLayout, PT_VideoMem ptVideoMem)
    {
    	T_PixelDatas tOriginIconPixelDatas;
    	T_PixelDatas tIconPixelDatas;
    	int iError;
    	PT_Layout atLayout = ptPageLayout->atLayout;
    
    	/* 描画数据: VideoMem中的页面数据未生成的情况下才执行下面操作 */
    	if (ptVideoMem->ePicState != PS_GENERATED)
    	{
    		ClearVideoMem(ptVideoMem, COLOR_BACKGROUND);
    
    		tIconPixelDatas.iBpp          = ptPageLayout->iBpp;
            /* 分配一个临时的内存, 它用来存放缩放后的图标数据 */
    		tIconPixelDatas.aucPixelDatas = malloc(ptPageLayout->iMaxTotalBytes);
    		if (tIconPixelDatas.aucPixelDatas == NULL)
    		{
    			return -1;
    		}
    
    		while (atLayout->strIconName)
    		{
                /* 取得图标文件的象素数据 */
    			iError = GetPixelDatasForIcon(atLayout->strIconName, &tOriginIconPixelDatas);
    			if (iError)
    			{
    				DBG_PRINTF("GetPixelDatasForIcon %s error!\n", atLayout->strIconName);
                    free(tIconPixelDatas.aucPixelDatas);
    				return -1;
    			}
     			tIconPixelDatas.iHeight = atLayout->iBotRightY - atLayout->iTopLeftY + 1;
    			tIconPixelDatas.iWidth  = atLayout->iBotRightX - atLayout->iTopLeftX+ 1;
    			tIconPixelDatas.iLineBytes  = tIconPixelDatas.iWidth * tIconPixelDatas.iBpp / 8;
    			tIconPixelDatas.iTotalBytes = tIconPixelDatas.iLineBytes * tIconPixelDatas.iHeight;
    
                /* 把原始的图标象素数据缩放为指定大小 */
     			PicZoom(&tOriginIconPixelDatas, &tIconPixelDatas);
    
                /* 把缩放后的图标数据,合并到VideoMem的指定区域 */
     			PicMerge(atLayout->iTopLeftX, atLayout->iTopLeftY, &tIconPixelDatas, &ptVideoMem->tPixelDatas);
    
                /* 释放原始的图标象素数据 */
     			FreePixelDatasForIcon(&tOriginIconPixelDatas);
     			atLayout++;
    		}
    
            /* 数据上面分配的临时内存 */
    		free(tIconPixelDatas.aucPixelDatas);
    		ptVideoMem->ePicState = PS_GENERATED;
    	}
    
    	return 0;
    }
    

    该函数的功能是从图标文件中解析出图像数据并将其放在指定区域,从而生成页面数据。函数接受两个参数,一个是包含多个图标的文件名和显示区域的ptPageLayout​​​​​,另一个是在VideoMem​​​​​里构造页面数据的ptVideoMem​​​​​。函数首先检查VideoMem​​​​​中的页面数据是否已生成,如果未生成则执行以下操作:清除VideoMem​​​​​,分配一个临时内存来存放缩放后的图标数据,循环遍历布局中的每个图标,获取图标文件的像素数据,将原始图标像素数据缩放为指定大小,将缩放后的图标数据合并到VideoMem​​​​​的指定区域,释放原始图标像素数据,最后释放临时内存并将VideoMem​​​​​的页面数据状态设置为已生成。函数返回值为0表示成功,其他值表示失败。

  5. GetPixelDatasForIcon函数:

    int GetPixelDatasForIcon(char *strFileName, PT_PixelDatas ptPixelDatas)
    {
        T_FileMap tFileMap;
        int iError;
        int iXres, iYres, iBpp;
    
        /* 图标存在 /etc/digitpic/icons */
        snprintf(tFileMap.strFileName, 128, "%s/%s", ICON_PATH, strFileName);
        tFileMap.strFileName[127] = '\0';
         // 将文件映射到内存
        iError = MapFile(&tFileMap);
        if (iError)
        {
            DBG_PRINTF("MapFile %s error!\n", strFileName);
            return -1;
        }
     	// 检查文件格式是否支持
        iError = Parser("bmp")->isSupport(&tFileMap);
        if (iError == 0)
        {
            DBG_PRINTF("can't support this file: %s\n", strFileName);
            UnMapFile(&tFileMap);
            return -1;
        }
    	// 获取显示分辨率和位深度
        GetDispResolution(&iXres, &iYres, &iBpp);
        ptPixelDatas->iBpp = iBpp;
    	 // 从文件中提取像素数据
        iError = Parser("bmp")->GetPixelDatas(&tFileMap, ptPixelDatas);
        if (iError)
        {
            DBG_PRINTF("GetPixelDatas for %s error!\n", strFileName);
            UnMapFile(&tFileMap);
            return -1;
        }
    	// 解除文件映射
        UnMapFile(&tFileMap);
        return 0;
    }
    
    

    该函数的功能是读取位于/etc/digitpic/icons目录下的BMP格式图标文件的像素数据,并将这些数据存储在通过malloc分配的空间中,该空间由函数的调用者在不需要时使用FreePixelDatasForIcon函数释放。函数返回0表示成功,返回其他值表示失败。函数内部使用了文件映射和解析器功能来处理文件,并获取显示分辨率和像素数据。

  6. ​​FlushVideoMemToDev​函数:

    void FlushVideoMemToDev(PT_VideoMem ptVideoMem)
    {
        // 如果ptVideoMem没有使用设备的帧缓冲,则调用显示设备的ShowPage函数显示图像
        if (!ptVideoMem->bDevFrameBuffer)
        {
            GetDefaultDispDev()->ShowPage(ptVideoMem);
        }
    }
    

    函数的作用是将存储在视频内存中的图像数据刷到显示设备上,使得用户可以观察到缓冲区中的图像。

  7. DBG_PRINTF函数:

    int DebugPrint(const char *pcFormat, ...)
    {
        char strTmpBuf[1000]; // 用于格式化后临时存储打印信息的缓冲区
        char *pcTmp;         // 指向处理后待打印字符串的指针
        va_list tArg;        // 可变参数列表处理
        int iNum;            // 格式化后字符串长度
        PT_DebugOpr ptTmp = g_ptDebugOprHead; // 指向调试操作链表头部的指针
        int dbglevel = DEFAULT_DBGLEVEL; // 调试信息级别
    
        /* 处理可变参数,格式化字符串到临时缓冲区 */
    	/* 可变参数的处理, 抄自glibc的printf函数 */
        va_start(tArg, pcFormat);
        iNum = vsprintf(strTmpBuf, pcFormat, tArg);
        va_end(tArg);
        strTmpBuf[iNum] = '\0';
    
        pcTmp = strTmpBuf;  
    
        /* 解析调试信息级别,决定是否打印 */
        if ((strTmpBuf[0] == '<') && (strTmpBuf[2] == '>'))
        {
            dbglevel = strTmpBuf[1] - '0';
            if (dbglevel >= 0 && dbglevel <= 9)
            {
                pcTmp = strTmpBuf + 3;
            }
            else
            {
                dbglevel = DEFAULT_DBGLEVEL;
            }
        }
    
        /* 若调试信息级别高于设置的限制,则不打印 */
        if (dbglevel > g_iDbgLevelLimit)
        {
            return -1;
        }
    
        /* 调用链表中所有可用的调试输出函数,输出调试信息 */
        while (ptTmp)
        {
            if (ptTmp->isCanUse)
            {
                ptTmp->DebugPrint(pcTmp);
            }
            ptTmp = ptTmp->ptNext;
        }
    
        return 0;
    
    }
    

    该函数是一个打印信息的总入口函数,用于处理程序中使用DBG_PRINTF​​​​宏来打印的信息。DBG_PRINTF​​​​的使用方法与printf​​​​完全一致。函数首先将格式化后的字符串存储到临时缓冲区,然后解析调试信息级别并决定是否打印。如果调试信息级别高于设置的限制,则不打印。最后,调用链表中所有可用的调试输出函数,输出调试信息。函数返回0表示打印成功,返回-1表示打印失败。

  8. GenericGetInputEvent函数:

    该函数的功能是读取输入数据(触摸屏事件),并判断该数据是否位于任何一个图标上。函数接收两个参数,一个是指向包含多个图标的显示区域的指针(ptPageLayout),另一个是指向包含得到的输入数据的结构体的指针(ptInputEvent)。函数返回-1表示输入数据不位于任何一个图标之上,返回其他值表示输入数据所落在的图标在PageLayout->atLayout数组中的索引。该函数首先调用GetInputEvent函数获取原始的触摸屏数据,然后检查事件类型是否为触摸屏事件。如果是,则将得到的输入数据赋值给ptInputEvent指向的结构体,并遍历所有图标,检查触点是否落在任一图标上。如果触点落在某个图标上,函数返回该图标在数组中的索引;否则,返回-1。

    int GenericGetInputEvent(PT_PageLayout ptPageLayout, PT_InputEvent ptInputEvent)
    {
    	T_InputEvent tInputEvent;
    	int iRet;
    	int i = 0;
    	PT_Layout atLayout = ptPageLayout->atLayout;
    
    	/* 获得原始的触摸屏数据 
    	 * 它是调用input_manager.c的函数,此函数会让当前线否休眠
    	 * 当触摸屏线程获得数据后,会把它唤醒
    	 */
    	iRet = GetInputEvent(&tInputEvent);
    	if (iRet)
    	{
    		return -1;
    	}
    	/* 如果事件类型不是触摸屏事件,则直接返回 */
    	if (tInputEvent.iType != INPUT_TYPE_TOUCHSCREEN)
    	{
    		return -1;
    	}
    
    	*ptInputEvent = tInputEvent;
    
    	/* 遍历所有图标,检查触点是否落在任一图标上 */
    	while (atLayout[i].strIconName)
    	{
    		if ((tInputEvent.iX >= atLayout[i].iTopLeftX) && (tInputEvent.iX <= atLayout[i].iBotRightX) && \
    			 (tInputEvent.iY >= atLayout[i].iTopLeftY) && (tInputEvent.iY <= atLayout[i].iBotRightY))
    		{
    			/* 找到了被点中的按钮 */
    			return i;
    		}
    		else
    		{
    			i++;
    		}
    	}
    
    	/* 触点没有落在按钮上 */
    	return -1;
    }
    
    
  9. browse页面生成目录和文件:

    该函数GenerateBrowsePageDirAndFile​主要用于为“浏览页面”生成“目录或文件”区域中的图标和文字,即显示目录内容。其功能包括:

    1. 根据输入参数,在屏幕上显示指定开始索引和数量的目录和文件内容。
    2. 使用指定的目录和文件内容数组aptDirContents​,在视频内存ptVideoMem​中构造页面。
    3. 清除即将显示目录和文件内容的区域。
    4. 设置字体大小,以适应显示目录或文件名的区域。
    5. 使用双层循环遍历并显示每个目录或文件,其中根据是目录还是文件显示相应的图标,并在相应位置显示目录或文件名。
    6. 返回成功标志0。

    该函数的输入参数包括起始索引iStartIndex​、目录和文件内容数组数量iDirContentsNumber​、目录和文件内容数组aptDirContents​、视频内存ptVideoMem​。

    该函数的输出参数为空。

    该函数的返回值为成功标志0,或其他值表示失败。

    static int GenerateBrowsePageDirAndFile(int iStartIndex, int iDirContentsNumber, PT_DirContent *aptDirContents, PT_VideoMem ptVideoMem)
    {
        int iError;
        int i, j, k = 0;
        int iDirContentIndex = iStartIndex;
        PT_PageLayout ptPageLayout = &g_tBrowsePageDirAndFileLayout;
        PT_Layout atLayout = ptPageLayout->atLayout;
    
        // 清除即将显示目录和文件内容的区域
        ClearRectangleInVideoMem(ptPageLayout->iTopLeftX, ptPageLayout->iTopLeftY, ptPageLayout->iBotRightX, ptPageLayout->iBotRightY, ptVideoMem, COLOR_BACKGROUND);
    
        // 设置字体大小,以适应显示目录或文件名的区域
        SetFontSize(atLayout[1].iBotRightY - atLayout[1].iTopLeftY - 5);
      
        // 双层循环以遍历并显示每个目录或文件
        for (i = 0; i < g_iDirFileNumPerCol; i++)
        {
            for (j = 0; j < g_iDirFileNumPerRow; j++)
            {
                if (iDirContentIndex < iDirContentsNumber)
                {
                    // 根据是目录还是文件,显示相应的图标
                    if (aptDirContents[iDirContentIndex]->eFileType == FILETYPE_DIR)
                    {
                        PicMerge(atLayout[k].iTopLeftX, atLayout[k].iTopLeftY, &g_tDirClosedIconPixelDatas, &ptVideoMem->tPixelDatas);
                    }
                    else
                    {
                        PicMerge(atLayout[k].iTopLeftX, atLayout[k].iTopLeftY, &g_tFileIconPixelDatas, &ptVideoMem->tPixelDatas);
                    }
    
                    k++;  // 为下一个图标或文件名准备位置
                    // 在相应位置显示目录或文件名
                    iError = MergerStringToCenterOfRectangleInVideoMem(atLayout[k].iTopLeftX, atLayout[k].iTopLeftY, atLayout[k].iBotRightX, atLayout[k].iBotRightY, (unsigned char *)aptDirContents[iDirContentIndex]->strName, ptVideoMem);
                    //ClearRectangleInVideoMem(atLayout[k].iTopLeftX, atLayout[k].iTopLeftY, atLayout[k].iBotRightX, atLayout[k].iBotRightY, ptVideoMem, 0xff0000);
                    k++;  // 为下一个文本行准备位置
    
                    iDirContentIndex++;  // 准备显示下一个目录或文件
                }
                else
                {
                    break;  // 如果已经处理完所有目录和文件内容,则跳出循环
                }
            }
            if (iDirContentIndex >= iDirContentsNumber)
            {
                break;  // 如果已经处理完所有目录和文件内容,则跳出循环
            }
        }
        return 0;
    }
    
  10. MergerStringToCenterOfRectangleInVideoMem函数:

    /**********************************************************************
     * 函数名称: MergerStringToCenterOfRectangleInVideoMem
     * 功能描述: 在VideoMem的指定矩形居中显示字符串
     *            参考: 03.freetype\02th_arm\06th_show_lines_center
     * 输入参数: iTopLeftX,iTopLeftY   - 矩形区域的左上角座标
     *            iBotRightX,iBotRightY - 矩形区域的右下角座标
     *            pucTextString         - 要显示的字符串
     *            ptVideoMem            - VideoMem
     * 输出参数: 无
     * 返 回 值: 0 - 成功,  其他值 - 失败
     * 修改日期        版本号     修改人          修改内容
     * -----------------------------------------------
     * 2013/02/08        V1.0     韦东山          创建
     ***********************************************************************/
    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;
    
    	tFontBitMap.iCurOriginX = 0;
    	tFontBitMap.iCurOriginY = 0;
    	pucBufStart = pucTextString;
    	pucBufEnd   = pucTextString + strlen((char *)pucTextString);   
    
    	/* 0. 清除这个区域 */
    	ClearRectangleInVideoMem(iTopLeftX, iTopLeftY, iBotRightX, iBotRightY, ptVideoMem, COLOR_BACKGROUND);
    
    	/* 1.先计算字符串显示的总体宽度、高度 */
    	while (1)
    	{
    		/* 从字符串中逐个取出字符 */
    		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
    		if (0 == iLen)
    		{
    			/* 字符串结束 */
    			if (!bHasGetCode)
    			{
    				//DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    				return -1;
    			}
    			else
    			{
    				break;
    			}
    		}
    
    		bHasGetCode = 1;
    		pucBufStart += iLen;
    
    		/* 获得字符的位图, 位图信息里含有字符显示时的左上角、右下角坐标 */
    		iError = GetFontBitmap(dwCode, &tFontBitMap);
    		if (0 == iError)
    		{			
    			// 更新最小/最大x、y坐标					
    			if (iMinX > tFontBitMap.iXLeft)
    			{
    				iMinX = tFontBitMap.iXLeft;
    			}
    			if (iMaxX < tFontBitMap.iXMax)
    			{
    				iMaxX = tFontBitMap.iXMax;
    			}
    
    			if (iMinY > tFontBitMap.iYTop)
    			{
    				iMinY = tFontBitMap.iYTop;
    			}
    			if (iMaxY < tFontBitMap.iXMax)
    			{
    				iMaxY = tFontBitMap.iYMax;
    			}
    		
    			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
    			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
    		}
    		else
    		{
    			DBG_PRINTF("GetFontBitmap for calc width/height error!\n");
    		}
    	}
    
    	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;
    	//DBG_PRINTF("iNewFirstFontTopLeftX = %d, iNewFirstFontTopLeftY = %d\n", iNewFirstFontTopLeftX, iNewFirstFontTopLeftY);
    
    	/*	 
    	 * 2.2 再计算第1个字符原点坐标
    	 * iMinX - 原来的iCurOriginX(0) = iStrTopLeftX - 新的iCurOriginX
    	 * iMinY - 原来的iCurOriginY(0) = iStrTopLeftY - 新的iCurOriginY
    	 */
    	tFontBitMap.iCurOriginX = iStrTopLeftX - iMinX;
    	tFontBitMap.iCurOriginY = iStrTopLeftY - iMinY;
    
    	//DBG_PRINTF("iCurOriginX = %d, iCurOriginY = %d\n", tFontBitMap.iCurOriginX, tFontBitMap.iCurOriginY);
    
    	pucBufStart = pucTextString;
    	bHasGetCode = 0;
    	while (1)
    	{
    		/* 从字符串中逐个取出字符 */
    		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
    		if (0 == iLen)
    		{
    			/* 字符串结束 */
    			if (!bHasGetCode)
    			{
    				DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    				return -1;
    			}
    			else
    			{
    				break;
    			}
    		}
    
    		bHasGetCode = 1;
    		pucBufStart += iLen;
    
    		/* 获得字符的位图 */
    		iError = GetFontBitmap(dwCode, &tFontBitMap);
    		if (0 == iError)
    		{
    			//DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    			// 判断字符是否在矩形区域内,若在则合并到VideoMem显示字符,否则返回0
                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;
                }
    			//DBG_PRINTF("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
    		
    			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
    			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
    		}
    		else
    		{
    			DBG_PRINTF("GetFontBitmap for drawing error!\n");
    		}
    	}
    
    	return 0;
    }
    

    函数名为MergerStringToCenterOfRectangleInVideoMem​,其功能是在VideoMem(视频内存)的指定矩形区域中居中显示字符串。以下是该函数的具体实现过程:

    1. 输入参数初始化

      • 接收5个输入参数:

        • iTopLeftX​和iTopLeftY​:矩形区域左上角坐标。
        • iBotRightX​和iBotRightY​:矩形区域右下角坐标。
        • pucTextString​:指向待显示字符串的指针。
        • ptVideoMem​:指向VideoMem结构体的指针。
      • 初始化一些局部变量,如最小/最大坐标值(iMinX​、iMaxX​、iMinY​、iMaxY​),字符串起始与结束指针(pucBufStart​、pucBufEnd​),以及字体位图结构体T_FontBitMap​。

    2. 清除指定矩形区域

      • 使用ClearRectangleInVideoMem​函数,以指定的矩形区域坐标和背景色,清除VideoMem中的对应区域。
    3. 计算字符串总体宽度和高度

      • 使用循环遍历字符串中的每个字符:

        • 通过GetCodeFrmBuf​函数从字符串中逐个获取字符编码。
        • 对于每个字符,调用GetFontBitmap​函数获取其位图信息(包括左上角、右下角坐标等)。
        • 更新最小/最大坐标值,以便计算字符串整体的宽度(iMaxX - iMinX​)和高度(iMaxY - iMinY​)。
    4. 调整字符串尺寸和定位第一个字符原点

      • 根据计算出的字符串宽度和矩形区域宽度,若字符串过宽,则将其宽度调整为矩形宽度。
      • 若字符串高度超过矩形高度,返回错误(-1)。
      • 计算居中显示时,第一个字符的左上角坐标(iStrTopLeftX​和iStrTopLeftY​)。
      • 根据新计算出的左上角坐标和原始位图信息,更新字体位图结构体中的当前原点坐标(tFontBitMap.iCurOriginX​和tFontBitMap.iCurOriginY​)。
    5. 绘制字符串到VideoMem

      • 重新设置字符串起始指针并重置相关标志。

      • 使用与步骤3相同的循环结构,再次遍历字符串中的每个字符:

        • 获取字符编码及位图信息。
        • 判断字符位图是否在矩形区域内,若在则调用MergeOneFontToVideoMem​函数将字符合并到VideoMem中显示;若不在则返回0。
        • 更新字体位图结构体中的当前原点坐标。
    6. 返回结果

      • 若所有字符均成功合并到VideoMem且无其他错误,返回0表示成功;否则返回非零值表示失败。
  11. GetCodeFrmBuf函数:

    /**********************************************************************
     * 函数名称: GetCodeFrmBuf
     * 功能描述: 从字符串中取出UTF8格式的字符串的UNICODE编码值
     * 输入参数: pucBufStart - 字符串起始位置
     *            pucBufEnd   - 字符串结束位置(这个位置的字符不处理)
     * 输出参数: pdwCode     - 所取出的编码值存在这里
     * 返 回 值: 使用了多少个字符才得到这个编码值
     * 修改日期        版本号     修改人	      修改内容
     * -----------------------------------------------
     * 2013/02/08	     V1.0	  韦东山	      创建
     ***********************************************************************/
    int GetCodeFrmBuf(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode)
    {
        return Encode("utf-8")->GetCodeFrmBuf(pucBufStart, pucBufEnd, pdwCode);
    }
    
    

标签:函数,iError,int,相框,数码,讲解,atLayout,输入,图标
From: https://www.cnblogs.com/rose24/p/18133251/digital-photo-frametest-z1xe04l

相关文章

  • 通俗易懂的KMP理论讲解(含手求Next数组)
    通俗易懂的KMP理论讲解(含手求Next数组)1.KMP算法介绍KMP算法的核心是利用匹配失败后的信息,通过一个next数组,保存模式串中前后最长公共子序列的长度,尽量减少模式串与主串的匹配次数降低时间复杂度以达到快速匹配的目的。2.字符串的前后缀与公共前后缀2.1字符串的前缀字符串的......
  • sql注入漏洞简单讲解与实战
    SQL注入(SQLInjection)是一种常见的网络安全漏洞,攻击者利用该漏洞向应用程序的数据库发送恶意的SQL查询,从而绕过身份验证、获取敏感数据或者对数据库进行修改。SQL注入通常发生在与数据库交互的Web应用程序中,尤其是那些直接将用户输入拼接到SQL查询语句中的应用程序。攻击者利......
  • Deep Deterministic Policy Gradient(DDPG)算法讲解笔记
    DDPGDeepDeterministicPolicyGradient,基于actor-critic模型提出了一个有效的valuebased连续型空间的RL算法,引入了一些帮助训练稳定的技术。基础:DQN,Batchnormm,Discretize,微积分backgroundDQN改进的推广Policybased方法(TRPO)已经在actionspace取得突破传统disc......
  • 数码相框-显示bmp图片
    写主函数和测试功能#include<unistd.h>#include<stdlib.h>#include<stdio.h>#include<config.h>#include<draw.h>#include<encoding_manager.h>#include<fonts_manager.h>#include<disp_manager.h>#include<inpu......
  • 毕业设计3020微信小程序-图书馆图书借阅预览预约管理软件【源代码+文档+调试+讲解视频
    摘要本文详细介绍了基于服务器与客户端模式的图书馆图书借阅预览预约管理软件的设计与实现过程。服务器端具备学生管理、图书管理、自习室管理、自习室预约管理、图书借阅管理以及系统管理等功能,而客户端则为用户提供注册登录、首页浏览、图书借阅、自习室预约以及个人信息......
  • CH573 CH582 CH592外设IAP例程讲解
    一.根据所选芯片型号下载官网最新例程,tips:若使用的是ch571这类codeflash是192k的芯片,需要将iap程序中的宏定义进行修改:源程序是0x0007000(448k),修改为0x0003000(192k)#defineAPP_CODE_END_ADDR0x00030000二.根据所选芯片型号下载对应的程序,三.串口1接usb转......
  • 【讲解下Fiddler的安装和使用】
    ......
  • 【讲解下如何从零基础学习Java】
    ......
  • 基于SpringBoot+Vue的4S店车辆管理系统(源码+文档+部署+讲解)
    一.系统概述随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各业相继进入信息管理时代,4S店车辆系统就是信息时代变革中的产物之一。任何系统都要遵循系......
  • jvm整理 ,讲解的很好。
    jvm整理 https://blog.csdn.net/qq_41433169/article/details/119811601 Interviewpreparation--JVM性能优化-垃圾收集器https://blog.csdn.net/liaojiamin0102/article/details/129320817......