数码相框项目笔记
编码及字体模块
对于一个浏览页面,除了文件图标之外还有文件名,这些字符的显示,主要有以下三步
- 1、获取编码值
- 2、根据编码值,去字体文件中找到对应的位图点阵
- 3、将位图点阵LCD描画出来
关于字符编码可以查看这几篇文章:
http://cenalulu.github.io/linux/character-encoding/
https://blog.csdn.net/qq_28098067/article/details/53486032
字符的编码值获得后再去字体文件中得到字形索引,根据字形索引获取位图
freetype是一个免费、开源、可移植且高质量的字体引擎, 支持多种字体格式文件,并提供了统一的访问接口。它不但可以处理点阵字体,也可以处理多种矢量字体。本项目主要使用freetype的函数来获得字符位图。
freetype的资料可以参考:
官方网站:
https://www.freetype.org/index.html
中文使用参考:
http://wenku.baidu.com/view/2d24be10cc7931b765ce155b.html
https://wenku.baidu.com/view/e7149f6748d7c1c708a14574.html
所以,编码模块主要是从不同格式的文件中获得编码值,字体模块主要是根据编码值从字体文件中获取字符的位图。
同样,编码模块和字体模块提供了管理模块,当添加不同的编码方式和字体时,向管理模块注册即可。
解析和渲染模块
解析模块:主要是从不同格式的图片文件中解析出RGB数据
例如:
*.bmp文件,它主要由三部分组成
1、文件信息头(BITMAPFILEHEADER):14字节
2、位图信息头(BITMAPINFOHEADER):40字节
3、RGB颜色阵列
这部分就是我们需要显示在屏幕上的数据。
因此,我们可以根据文件信息头和位图信息头来确定图片的大小、像素位数、RGB数据的位置等。
参考博客:https://www.cnblogs.com/CZM-/p/5388553.html
对于JPG格式的图片,它是经过压缩的图片,我们可以使用libjpeg库提供的函数来解压出RGB原始数据。
具体流程如下:
Allocate and initialize a JPEG decompression object // 分配和初始化一个decompression 结构体
(1)Specify the source of the compressed data (eg, a file) // 提定源文件
(2)Call jpeg_read_header() to obtain image info // 用jpeg_read_header获得jpg信息
(3)Set parameters for decompression // 设置解压参数,比如放大、缩小
(4)jpeg_start_decompress(...); // 启动解压:jpeg_start_decompress
(5)while (scan lines remain to be read)
jpeg_read_scanlines(...); // 循环调用jpeg_read_scanlines
(6)jpeg_finish_decompress(...); // jpeg_finish_decompress
(7)Release the JPEG decompression object // 释放decompression结构体
同样的,解析模块也提供了管理模块,当添加不同的图片格式解析操作时,向其注册即可。
渲染模块:
把图像显示到屏幕上,即将解析后的RGB数据写入显存
图片操作模块
图片的缩放和合并操作
zoom.c
#include <config.h>
#include <pic_operation.h>
#include <stdlib.h>
#include <string.h>
/* 图片缩放 */
int PicZoom(PT_PixelDatas ptOriginPic, PT_PixelDatas ptZoomPic)
{
unsigned long dwDstWidth = ptZoomPic->iWidth;
unsigned long* pdwSrcXTable;
unsigned long x;
unsigned long y;
unsigned long dwSrcY;
unsigned char *pucDest;
unsigned char *pucSrc;
unsigned long dwPixelBytes = ptOriginPic->iBpp/8;
if (ptOriginPic->iBpp != ptZoomPic->iBpp)
{
return -1;
}
pdwSrcXTable = malloc(sizeof(unsigned long) * dwDstWidth);
if (NULL == pdwSrcXTable)
{
printf("malloc error!\n");
return -1;
}
for (x = 0; x < dwDstWidth; x++)//生成表 pdwSrcXTable
{
pdwSrcXTable[x]=(x*ptOriginPic->iWidth/ptZoomPic->iWidth);
}
for (y = 0; y < ptZoomPic->iHeight; y++)
{
dwSrcY = (y * ptOriginPic->iHeight / ptZoomPic->iHeight);
pucDest = ptZoomPic->aucPixelDatas + y*ptZoomPic->iLineBytes;
pucSrc = ptOriginPic->aucPixelDatas + dwSrcY*ptOriginPic->iLineBytes;
for (x = 0; x <dwDstWidth; x++)
{
/* 原图座标: pdwSrcXTable[x],srcy
* 缩放座标: x, y
*/
memcpy(pucDest+x*dwPixelBytes, pucSrc+pdwSrcXTable[x]*dwPixelBytes, dwPixelBytes);
}
}
free(pdwSrcXTable);
return 0;
}
merge.c
#include <pic_operation.h>
#include <string.h>
/* 小图片合并进大图片里 */
int PicMerge(int iX, int iY, PT_PixelDatas ptSmallPic, PT_PixelDatas ptBigPic)
{
int i;
unsigned char *pucSrc;
unsigned char *pucDst;
if ((ptSmallPic->iWidth > ptBigPic->iWidth) ||
(ptSmallPic->iHeight > ptBigPic->iHeight) ||
(ptSmallPic->iBpp != ptBigPic->iBpp))
{
return -1;
}
pucSrc = ptSmallPic->aucPixelDatas;
pucDst = ptBigPic->aucPixelDatas + iY * ptBigPic->iLineBytes + iX * ptBigPic->iBpp / 8;
for (i = 0; i < ptSmallPic->iHeight; i++)
{
memcpy(pucDst, pucSrc, ptSmallPic->iLineBytes);
pucSrc += ptSmallPic->iLineBytes;
pucDst += ptBigPic->iLineBytes;
}
return 0;
}
/* 新图片合并进老图片指定区域 */
int PicMergeRegion(int iStartXofNewPic, int iStartYofNewPic, int iStartXofOldPic, int iStartYofOldPic, int iWidth, int iHeight, PT_PixelDatas ptNewPic, PT_PixelDatas ptOldPic)
{
int i;
unsigned char *pucSrc;
unsigned char *pucDst;
int iLineBytesCpy = iWidth * ptNewPic->iBpp / 8;
if ((iStartXofNewPic < 0 || iStartXofNewPic >= ptNewPic->iWidth) || \
(iStartYofNewPic < 0 || iStartYofNewPic >= ptNewPic->iHeight) || \
(iStartXofOldPic < 0 || iStartXofOldPic >= ptOldPic->iWidth) || \
(iStartYofOldPic < 0 || iStartYofOldPic >= ptOldPic->iHeight))
{
return -1;
}
pucSrc = ptNewPic->aucPixelDatas + iStartYofNewPic * ptNewPic->iLineBytes + iStartXofNewPic * ptNewPic->iBpp / 8;
pucDst = ptOldPic->aucPixelDatas + iStartYofOldPic * ptOldPic->iLineBytes + iStartXofOldPic * ptOldPic->iBpp / 8;
for (i = 0; i < iHeight; i++)
{
memcpy(pucDst, pucSrc, iLineBytesCpy);
pucSrc += ptNewPic->iLineBytes;
pucDst += ptOldPic->iLineBytes;
}
return 0;
}
文件处理模块
采用面向对象设计的思想,我们将文件的操作整合到一个模块中。此模块主要包括各种文件的操作:文件打开、内存映射、内存释放、获取目录内容等。
当我们在浏览界面上点击目录时,会进入到目录中去,这时便要显示此目录中的内容,因此我们需要在用户点击目录时获取到点击目录下的内容。
file.h
#ifndef _FILE_H
#define _FILE_H
#include <stdio.h>
//FileMap结构体
typedef struct FileMap {
char strFileName[256]; /* 文件名 */
// int iFd;
FILE * tFp; /* 文件句柄 */
int iFileSize; /* 文件大小 */
unsigned char *pucFileMapMem; /* 使用mmap函数映射文件得到的内存 */
}T_FileMap, *PT_FileMap;
/* 文件类别 */
typedef enum {
FILETYPE_DIR = 0, /* 目录 */
FILETYPE_FILE, /* 文件 */
}E_FileType;
/* 目录里的内容 */
typedef struct DirContent {
char strName[256]; /* 名字 */
E_FileType eFileType; /* 类别 */
}T_DirContent, *PT_DirContent;
/* 将一个文件映射到内存 mmap */
int MapFile(PT_FileMap ptFileMap);
/* munmap */
void UnMapFile(PT_FileMap ptFileMap);
/* 把某目录下所含的顶层子目录、顶层文件都记录下来,并且按名字排序 */
int GetDirContents(char *strDirName, PT_DirContent **pptDirContents, int *piNumber);
/* 释放内存 */
void FreeDirContents(PT_DirContent *aptDirContents, int iNumber);
/* DFS获得目录下的文件
* 即:先获得顶层目录下的文件, 再进入一级子目录A
* 再获得一级子目录A下的文件, 再进入二级子目录AA, ...
* 处理完一级子目录A后, 再进入一级子目录B
*
* "连播模式"下调用该函数获得要显示的文件
* 有两种方法获得这些文件:
* 1. 事先把所有文件的名字保存到某个缓冲区中
* 2. 用到时再去搜索取出若干个文件名
* 第1种方法比较简单,但是当文件很多时有可能导致内存不足.
* 我们使用第2种方法:
* 假设某目录(包括所有子目录)下所有的文件都给它编一个号
*/
int GetFilesIndir(char *strDirName, int *piStartNumberToRecord, int *piCurFileNumber, int *piFileCountHaveGet, int iFileCountTotal, char apstrFileNames[][256]);
#endif
file.c
...
...
/* 判断一个文件是否为目录
取得指定文件的文件属性,文件属性存储在结构体stat里。
int stat(const char *pathname, struct stat *statbuf);
*/
static int isDir(char *strFilePath, char *strFileName)
{
char strTmp[256];
struct stat tStat;
snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);
strTmp[255] = '\0';
if ((stat(strTmp, &tStat) == 0) && S_ISDIR(tStat.st_mode))
{
return 1;
}
else
{
return 0;
}
}
/* 判断一个目录是否常规的目录,在本程序中把sbin等目录当作特殊目录来对待 */
static int isRegDir(char *strDirPath, char *strSubDirName)
{
static const char *astrSpecailDirs[] = {"sbin", "bin", "usr", "lib", "proc", "tmp", "dev", "sys", NULL};
int i;
/* 如果目录名含有"astrSpecailDirs"中的任意一个, 则返回0 */
if (0 == strcmp(strDirPath, "/"))
{
while (astrSpecailDirs[i])
{
if (0 == strcmp(strSubDirName, astrSpecailDirs[i]))
return 0;
i++;
}
}
return 1;
}
/* 判断一个文件是否常规的文件,设备节点/链接文件/FIFO文件等是特殊文件 */
static int isRegFile(char *strFilePath, char *strFileName)
{
char strTmp[256];
struct stat tStat;
snprintf(strTmp, 256, "%s/%s", strFilePath, strFileName);
strTmp[255] = '\0';
if ((stat(strTmp, &tStat) == 0) && S_ISREG(tStat.st_mode))
{
return 1;
}
else
{
return 0;
}
}
/* 把某目录下所含的顶层子目录、顶层文件都记录下来,并且按名字排序 */
int GetDirContents(char *strDirName, PT_DirContent **pptDirContents, int *piNumber)
{
PT_DirContent *aptDirContents;
struct dirent **aptNameList;
int iNumber;
int i;
int j;
/* 扫描目录,结果按名字排序,存在aptNameList[0],aptNameList[1],...
* #include <dirent.h>
* 目录操作函数scandir
* int scandir(const char *dir, struct dirent **namelist, nt (*select) (const struct dirent *), nt (*compar) (const struct dirent **, const struct dirent**));
* 函数说明:scandir()会扫描参数dir指定的目录文件,经由参数select指定的函数来挑选目录结构至参数namelist数组中,最后再调用参数compar指定的函数来排序namelist数组中的目录数据。
* 每次从目录文件中读取一个目录结构后便将此结构传给参数select所指的函数, select函数若不想要将此目录结构复制到namelist数组就返回0,若select为空指针则代表选择所有的目录结构。scandir()会调用qsort()来排序数据,参数compar则为qsort()的参数,若是要排列目录名称字母则可使用alphasort(). 结构dirent定义请参考readdir()
* 返回值 :成功则返回复制到namelist数组中的数据结构数目,有错误发生则返回-1
*/
iNumber = scandir(strDirName, &aptNameList, 0, alphasort);
if (iNumber < 0)
{
printf("scandir error : %s!\n", strDirName);
return -1;
}
/* 忽略".", ".."这两个目录 */
aptDirContents = malloc(sizeof(PT_DirContent) * (iNumber - 2));
if (NULL == aptDirContents)
{
printf("malloc error!\n");
return -1;
}
*pptDirContents = aptDirContents;
for (i = 0; i < iNumber - 2; i++)
{
aptDirContents[i] = malloc(sizeof(T_DirContent));
if (NULL == aptDirContents)
{
printf("malloc error!\n");
return -1;
}
}
/* 先把目录挑出来存入aptDirContents */
for (i = 0, j = 0; i < iNumber; i++)
{
/* 忽略".",".."这两个目录 */
if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
continue;
/* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
/* if (aptNameList[i]->d_type == DT_DIR) */
if (isDir(strDirName, aptNameList[i]->d_name))
{
strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
aptDirContents[j]->strName[255] = '\0';
aptDirContents[j]->eFileType = FILETYPE_DIR;
free(aptNameList[i]);
aptNameList[i] = NULL;
j++;
}
}
/* 再把常规文件挑出来存入aptDirContents */
for (i = 0; i < iNumber; i++)
{
if (aptNameList[i] == NULL)
continue;
/* 忽略".",".."这两个目录 */
if ((0 == strcmp(aptNameList[i]->d_name, ".")) || (0 == strcmp(aptNameList[i]->d_name, "..")))
continue;
/* 并不是所有的文件系统都支持d_type, 所以不能直接判断d_type */
/* if (aptNameList[i]->d_type == DT_REG) */
if (isRegFile(strDirName, aptNameList[i]->d_name))
{
strncpy(aptDirContents[j]->strName, aptNameList[i]->d_name, 256);
aptDirContents[j]->strName[255] = '\0';
aptDirContents[j]->eFileType = FILETYPE_FILE;
free(aptNameList[i]);
aptNameList[i] = NULL;
j++;
}
}
/* 释放aptDirContents中未使用的项 */
for (i = j; i < iNumber - 2; i++)
{
free(aptDirContents[i]);
}
/* 释放scandir函数分配的内存 */
for (i = 0; i < iNumber; i++)
{
if (aptNameList[i])
{
free(aptNameList[i]);
}
}
free(aptNameList);
*piNumber = j;
return 0;
}
/* 以深度优先的方式获得目录下的文件 */
int GetFilesIndir(char *strDirName, int *piStartNumberToRecord, int *piCurFileNumber, int *piFileCountHaveGet, int iFileCountTotal, char apstrFileNames[][256])
{
int ret;
PT_DirContent *aptDirContents; /* 数组:存有目录下"顶层子目录","文件"的名字 */
int iDirContentsNumber; /* aptDirContents数组有多少项 */
int i;
char strSubDirName[256];
/* 为避免访问的目录互相嵌套, 设置能访问的目录深度为10 */
#define MAX_DIR_DEEPNESS 10
static int iDirDeepness = 0;
if (iDirDeepness > MAX_DIR_DEEPNESS)
{
return -1;
}
iDirDeepness++;
ret = GetDirContents(strDirName, &aptDirContents, &iDirContentsNumber);
if (ret)
{
printf("GetDirContents error!\n");
iDirDeepness--;
return -1;
}
/* 先记录文件 */
for (i = 0; i < iDirContentsNumber; i++)
{
if (aptDirContents[i]->eFileType == FILETYPE_FILE)
{
if (*piCurFileNumber >= *piStartNumberToRecord)
{
snprintf(apstrFileNames[*piFileCountHaveGet], 256, "%s/%s", strDirName, aptDirContents[i]->strName);
(*piFileCountHaveGet)++;
(*piCurFileNumber)++;
(*piStartNumberToRecord)++;
if (*piFileCountHaveGet >= iFileCountTotal)
{
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}
}
else
{
(*piCurFileNumber)++;
}
}
}
/* 递归处理目录 */
for (i = 0; i < iDirContentsNumber; i++)
{
if ((aptDirContents[i]->eFileType == FILETYPE_DIR) && isRegDir(strDirName, aptDirContents[i]->strName))
{
snprintf(strSubDirName, 256, "%s/%s", strDirName, aptDirContents[i]->strName);
GetFilesIndir(strSubDirName, piStartNumberToRecord, piCurFileNumber, piFileCountHaveGet, iFileCountTotal, apstrFileNames);
if (*piFileCountHaveGet >= iFileCountTotal)
{
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}
}
}
FreeDirContents(aptDirContents, iDirContentsNumber);
iDirDeepness--;
return 0;
}