链接
- JZ2440 数码相框项目 扩展项目介绍
- JZ2440 数码相框项目 扩展项目(一) 多文件图标 (二) 显示png
- JZ2440 数码相框项目 扩展项目(三) 支持鼠标
- JZ2440 实现截图 保存为png格式
- JZ2440 数码相框项目 扩展项目 1-4 源码下载
扩展项目四
1.目标
"manual页面"里,点击"上一张"或"下一张"时所要显示的图片比较大, 速度有点慢: 改进它
2.分析
在打开当前图片后,就新建线程开始读取下一幅图片,若点击“下一张”按钮,获取之前线程读取的图片数据,就可以立马显示出来。
3.实现
在 manual_page.c 中包含头文件 <pthread.h>
使用 <pthread_create> <pthread_exit> <pthread_join> 这几个函数就可以实现功能
关于 pthread_create 的第二个参数
#include <pthread.h>
extern int pthread_create ( // 创建进程 创建成功返回 0
pthread_t *__restrict __newthread, // 参数1: 返回新建线程号
__const pthread_attr_t *__restrict __attr, // 参数2: 参考上面链接,一般填 NULL
void *(*__start_routine) (void *), // 参数3: 线程执行的函数
void *__restrict __arg) __THROW __nonnull ((1, 3)); // 参数4: 传递给线程函数的参数
extern void pthread_exit ( // 新建线程退出,并返回值
void *__retval) __attribute__ ((__noreturn__)); // 参数为线程执行完后的返回值
extern int pthread_join ( // 获取新建线程的返回结果,该函数会阻塞(非阻塞:pthread_detach)
pthread_t __th, // 要获取返回结果的线程号
void **__thread_return); // pthread_exit 中的返回值
先设计预读图片的线程函数:
static void* StartNextPicture(void* filename) // 传递进来参数类型为 char *
{
PT_PixelDatas picture_data = malloc(sizeof(T_PixelDatas)); // 要返回的图片数据
if (!picture_data) {
DBG_PRINTF("StartNextPicture picture_data malloc error!\n");
pthread_exit(NULL); // 失败返回空
}
DBG_PRINTF(" <Start Parse> %s!\n", filename);
if(GetPixelDatasFrmFile(filename, picture_data)) { // 读取图片数据,该函数可重入
DBG_PRINTF("<fault> can't open %s!\n", filename);
free(picture_data);
pthread_exit(NULL); // 失败返回空
}
DBG_PRINTF(" <Parse OK> %s!\n", filename);
pthread_exit(picture_data); // 成功返回图片数据
}
然后在主线程中创建该线程,然后等待返回结果就可以了,
只给出关键部分:
static void ManualPageRun(PT_PageParams ptParentPageParams)
{
pthread_t next_pthread_t; // 预读线程线程号
PT_PixelDatas next_picture_data; // 保存预读线程的返回结果
... // 其他声明
/* 显示菜单和图片文件 */ // 后面介绍第三个参数
ShowManualPage(&g_tManualPageMenuIconsLayout, strFullPathName, NULL);
findNextFile(strFullPathName); // 在该路径下循环找下一幅图片
pthread_create(&next_pthread_t, NULL, StartNextPicture, strFullPathName); // 创建预读线程
while (1) {
/* 先确定是否触摸了菜单图标 */ // 获取输入事件
iIndex = ManualPageGetInputEvent(&g_tManualPageMenuIconsLayout, &tInputEvent);
if (iIndex == NEXT_PICTURE) { // 如果点击了 “下一张” 按钮
// 获取预读线程返回结果,如果线程还没有执行完,会在此处阻塞
if(pthread_join(next_pthread_t, (void**)&next_picture_data)) {
DBG_PRINTF("pthread_join error\n");
break; // 失败退出
}
if (next_picture_data) // 返回的值非空显示图片
ShowPictureInManualPage(ptDevVideoMem, NULL, next_picture_data);
findNextFile(strFullPathName); // 在该路径下循环找下一幅图片
pthread_create(&next_pthread_t, NULL, StartNextPicture, strFullPathName); // 创建预读线程
}
else { ... // 其他事件处理 }
}
}
由于原有程序中并没有能把预读出来的数据直接显示出来的函数,为了改动尽量的小,所以给该函数添加了第三个参数(包括调用该函数的函数,该函数调用的函数),如果第三个参数传入非空,直接使用第三个参数来显示,第二个参数不起作用。
static int ShowPictureInManualPage(PT_VideoMem ptVideoMem, char *strFileName);
改为
static int ShowPictureInManualPage(PT_VideoMem ptVideoMem, char *strFileName,
PT_PixelDatas pic_data);
串口显示 < Parse OK > 后,再点击 “下一张” 按钮就可以瞬间显示出来,当然如果在线程还没有完成前就点击仍然会阻塞在 pthread_join 函数。
< Start Parse > //xhr/app/7_pngtest/lenna.jpg!
GetPixelDatasFrmFile 7 jpg //xhr/app/7_pngtest/lenna.jpg
< Parse OK > //xhr/app/7_pngtest/lenna.jpg!
说明
程序中,我只实现了 “下一张” 的预读,如果想要实现 “上一张” 的功能如法炮制即可,不过要注意线程中分配的内存释放问题,防止内存泄漏。