数码相框项目学习笔记(二)

数码相框项目笔记

编码及字体模块

对于一个浏览页面,除了文件图标之外还有文件名,这些字符的显示,主要有以下三步

  • 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;
}

猜你喜欢

转载自blog.csdn.net/ChenNightZ/article/details/108257662
今日推荐