测试
在makefile加上这句话意味着把警告当成错误处理:
这里看视频跟着来就好了。
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代码实现了一个输入事件管理系统,主要包括以下功能:
-
输入操作注册与显示:
-
RegisterInputOpr()
函数用于注册一个新的输入操作到全局链表g_ptInputOprHead
中。 -
ShowInputOpr()
函数遍历并打印已注册的所有输入操作的编号和名称。
-
-
输入事件线程与同步机制:
- 定义了全局变量
g_tInputEvent
存储当前输入事件,以及互斥锁g_tMutex
和条件变量g_tConVar
用于线程间同步。 -
InputEventTreadFunction()
是一个输入事件处理线程,它负责从外部获取输入事件,并通过条件变量通知主线程。互斥锁用于保护对全局输入事件变量的访问。 -
GetInputEvent()
函数等待并获取输入事件。当有事件发生时,它会被输入事件线程唤醒,并从全局变量中获取输入事件。
- 定义了全局变量
-
输入设备初始化与事件处理:
-
AllInputDevicesInit()
函数遍历输入操作链表,对每个设备调用其DeviceInit()
函数进行初始化。若初始化成功,为该设备创建一个输入事件处理线程(调用InputEventTreadFunction()
)。 -
InputInit()
函数负责初始化标准输入设备(如键盘)和触摸屏等输入设备,并返回初始化过程中的错误码。
-
整体来看,这段代码构建了一个支持多输入设备、多输入操作的输入事件管理系统。通过注册输入操作,可以将不同类型的输入事件关联到相应的处理逻辑。系统使用线程和同步机制确保输入事件的高效、安全地传递与处理。最后,通过初始化函数设置好各个输入设备,使得整个系统能够正常运行。
选择并初始化默认设备
/**
* 选择并初始化默认显示设备
* @param name 指向要选择的显示设备名称的字符指针。
* 注意:该函数不返回任何值。
*/
void SelectAndInitDefaultDispDev(char *name)
{
// 根据提供的设备名称获取显示操作句柄
g_ptDefaultDispOpr = GetDispOpr(name);
if (g_ptDefaultDispOpr)
{
// 如果找到设备,则初始化设备
g_ptDefaultDispOpr->DeviceInit();
// 清除屏幕,准备显示
g_ptDefaultDispOpr->CleanScreen(0);
}
}
该函数用于选择并初始化默认的显示设备。函数的参数是指向要选择的显示设备名称的字符指针。函数内部根据提供的设备名称获取显示操作句柄,如果找到该设备,则对其进行初始化,并清除屏幕准备显示。
在开发板上测试
安装触摸屏驱动,设置环境变量:
新建图标目录,放入图标:
运行程序:
修改按钮按下的取反函数
像素跨越多个字节的情况:
通常情况下,一个像素的颜色由若干个字节(通常为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; /* 移动到下一行的起始位置 */
}
}
项目效果演示
效果演示
效果演示:
屏幕校准:
编译使用最终版的数码相框
编译移植tslib:
韦东山 数码相框 项目学习(六)tslib的移植_tslib 源码包-CSDN博客
如果内核比较新,需要修改头文件,不然用不了:
驱动程序需要看这一节内容:
完整代码讲解
修改main_page
模块介绍
debug模块:
int DebugInit(void)
{
int iError;
iError = StdoutInit();
iError |= NetPrintInit();
return iError;
}
通过调用StdoutInit
和NetPrintInit
两个函数来初始化不同的调试输出通道,可以视为对特定功能(调试输出的初始化)进行了封装。这两个函数内部可能各自负责设置和配置其对应的输出机制(标准输出或网络打印),对外提供统一的接口,隐藏了实现细节。
display模块:
input模块:
要使用标准输入,必须要位于要程序位于前台。
font模块:
数码相框并没有用到asci i和gbk。
render模块:
page模块:
file模块:
encoding模块:
流程介绍
-
初始化模块数据:
/* 注册调试通道 */ 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();
-
"主页面"的运行函数:
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]); } } } } }
该函数是一个“主页面”的运行函数,主要实现了以下功能:
- 在页面上显示菜单图标。
- 根据用户输入事件进行相应处理,包括按下和松开按钮等。
- 根据用户选择,调用相应的功能页面并执行相应的操作,如浏览、连播和设置等。
- 在从设置页面返回后,显示当前的主页面。
该函数通过循环获取输入事件,并根据事件类型进行相应的处理。在按下按钮时,记录按下按钮的索引,并在松开按钮时,根据按下和松开的按钮是否相同来执行相应的操作。如果相同,则根据按钮索引调用相应的功能页面,并在返回后显示主页面。
-
显示页面:
/* 运行主页面 */ 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
参数获取页面布局信息,其中包括多个图标的文件名和显示区域。函数主要执行以下操作:- 调用
GetVideoMem
函数获取显示主页面所需的显存。 - 若获取显存失败,则打印错误信息并返回。
- 调用
CalcMainPageLayout
函数计算各图标的坐标(如果之前未计算过)。 - 调用
GeneratePage
函数根据页面布局信息和显存生成页面。 - 调用
FlushVideoMemToDev
函数将生成的页面刷到设备上去。 - 调用
PutVideoMem
函数释放显存。
- 调用
-
生成页面函数:
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表示成功,其他值表示失败。 -
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表示成功,返回其他值表示失败。函数内部使用了文件映射和解析器功能来处理文件,并获取显示分辨率和像素数据。
-
FlushVideoMemToDev函数:
void FlushVideoMemToDev(PT_VideoMem ptVideoMem) { // 如果ptVideoMem没有使用设备的帧缓冲,则调用显示设备的ShowPage函数显示图像 if (!ptVideoMem->bDevFrameBuffer) { GetDefaultDispDev()->ShowPage(ptVideoMem); } }
函数的作用是将存储在视频内存中的图像数据刷到显示设备上,使得用户可以观察到缓冲区中的图像。
-
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表示打印失败。 -
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; }
-
browse页面生成目录和文件:
该函数
GenerateBrowsePageDirAndFile
主要用于为“浏览页面”生成“目录或文件”区域中的图标和文字,即显示目录内容。其功能包括:- 根据输入参数,在屏幕上显示指定开始索引和数量的目录和文件内容。
- 使用指定的目录和文件内容数组
aptDirContents
,在视频内存ptVideoMem
中构造页面。 - 清除即将显示目录和文件内容的区域。
- 设置字体大小,以适应显示目录或文件名的区域。
- 使用双层循环遍历并显示每个目录或文件,其中根据是目录还是文件显示相应的图标,并在相应位置显示目录或文件名。
- 返回成功标志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; }
-
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(视频内存)的指定矩形区域中居中显示字符串。以下是该函数的具体实现过程:-
输入参数初始化:
-
接收5个输入参数:
-
iTopLeftX
和iTopLeftY
:矩形区域左上角坐标。 -
iBotRightX
和iBotRightY
:矩形区域右下角坐标。 -
pucTextString
:指向待显示字符串的指针。 -
ptVideoMem
:指向VideoMem结构体的指针。
-
-
初始化一些局部变量,如最小/最大坐标值(
iMinX
、iMaxX
、iMinY
、iMaxY
),字符串起始与结束指针(pucBufStart
、pucBufEnd
),以及字体位图结构体T_FontBitMap
。
-
-
清除指定矩形区域:
- 使用
ClearRectangleInVideoMem
函数,以指定的矩形区域坐标和背景色,清除VideoMem中的对应区域。
- 使用
-
计算字符串总体宽度和高度:
-
使用循环遍历字符串中的每个字符:
- 通过
GetCodeFrmBuf
函数从字符串中逐个获取字符编码。 - 对于每个字符,调用
GetFontBitmap
函数获取其位图信息(包括左上角、右下角坐标等)。 - 更新最小/最大坐标值,以便计算字符串整体的宽度(
iMaxX - iMinX
)和高度(iMaxY - iMinY
)。
- 通过
-
-
调整字符串尺寸和定位第一个字符原点:
- 根据计算出的字符串宽度和矩形区域宽度,若字符串过宽,则将其宽度调整为矩形宽度。
- 若字符串高度超过矩形高度,返回错误(-1)。
- 计算居中显示时,第一个字符的左上角坐标(
iStrTopLeftX
和iStrTopLeftY
)。 - 根据新计算出的左上角坐标和原始位图信息,更新字体位图结构体中的当前原点坐标(
tFontBitMap.iCurOriginX
和tFontBitMap.iCurOriginY
)。
-
绘制字符串到VideoMem:
-
重新设置字符串起始指针并重置相关标志。
-
使用与步骤3相同的循环结构,再次遍历字符串中的每个字符:
- 获取字符编码及位图信息。
- 判断字符位图是否在矩形区域内,若在则调用
MergeOneFontToVideoMem
函数将字符合并到VideoMem中显示;若不在则返回0。 - 更新字体位图结构体中的当前原点坐标。
-
-
返回结果:
- 若所有字符均成功合并到VideoMem且无其他错误,返回0表示成功;否则返回非零值表示失败。
-
-
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