首页 > 技术文章 > 【项目1_电子书】第3课、3.2节_电子书之代码阅读及编写

xiaohujian 2019-09-04 21:54 原文

主 机:VMWare--Ubuntu-16.04.2-x64-100ask
开发板:Mini2440--256M NandFlash,   2M NorFlash,   64M SDRAM,   LCD-TD35;
    bootlorder:u-boot1.16,      Kernel:4.3.2;
编译器:arm-linux-gcc-4.3.2


 

1、郝斌C语言回顾:
链表专业术语:
首结点:
存放第一个有效数据的结点

尾结点:
存放最后一个有效数据的结点,指针域的指针为NULL,尾结点的标志

头结点:
头结点的数据类型和首结点的类型是一模一样的
头结点是首结点前面的那个节点
头结点并不存在有效数据
设置头结点的目的是为了方便对链表的操作
头指针:
存放头结点地址的指针变量

确定一个链表需要一个参数,头指针
注1:对于每个链表元素,分为左右两部分,左边为数据单元,右边为下一元素地址。

struct Node
{
int data; //数据域
struct Node * pNext; //指针域
};
小测试:验证0、1、'0'作为判断条件的真假是否为真!
#include <stdio.h>
int main(void)
{
int red = 7;
char c;

red = (red>>2)<<3; //7=0b0000 1011,则7>>2=0b0000 0010=2,溢出两位; 2<<3=0b0001 0000=8;
printf("red = %d\n", red);
if(0)
{
printf("验证0作为判断条件的真假为真\n");
}
if(NULL)
{
printf("验证NULL作为判断条件的真假为真\n");
}
if(1)
{
printf("验证1作为判断条件的真假为真\n");
}
if('0')
{
printf("验证'0'作为判断条件的真假为真\n");
}
red = 0;
printf("已知red = 0, 则red = %#x\n", red);
c = '0';
printf("已知c = '0', 则c = %#x\n", c);
return 0;
}
/*
打印结果是:
red = 8
验证1作为判断条件的真假为真
验证'0'作为判断条件的真假为真
已知red = 0, 则red = 0
已知c = '0', 则c = 0x30
Press any key to continue
*/

 

2、关于函数memset(s, c, count)的定义:
void *memset(void *s, int c, size_t count)
{
char *xs = s;
while (count--)
*xs++ = c;
return s;
}
由此可知,函数memset()只能用来对字符型指针(char *类型)指向的空间进行赋值为c;
而对于短整型指针(short *类型)或整型指针(int *类型)指向的空间进行赋值为c, 则会发生错误;

 

3、关于成员iXres、iYres存在意义?
typedef struct DispOpr {
char *name;
int iXres;
int iYres;
int iBpp;
int (*DeviceInit)(void);
int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor);
int (*CleanScreen)(unsigned int dwBackColor);
struct DispOpr *ptNext;
}T_DispOpr, *PT_DispOpr;
答:<1>若最底层的驱动程序时fb.c,则:
g_fb_display_operation.xres = g_var.xres;
g_fb_display_operation.yres = g_var.yres;
g_fb_display_operation.bpp = g_var.bits_per_pixel;
g_fbmem = (unsigned char *)mmap(NULL, g_fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, g_fd_fb, 0);
因此,在结构体struct DispOpr中,有无iXres、iYres成员都可以,可用g_var.xres/yres取代。
<2>若最底层的驱动程序时crt.c,则:
程序将在PC机服务器终端显示需要打印的字符串。将需要成员iXres、iYres来规整显示区的长宽。

 

4、点阵
ascii.c: 8x16点阵
GBK: HZK16, 16x16点阵
freetype: 点阵: ①MONO位图(单色位图):1bit表示一个像素;
②anti-color(彩色(反色)位图): 1byte表示一个像素;

 

5、前代码解析
/* 二、显示矢量字体 */
error = FT_Init_FreeType( &library ); /* 初始化 Freetype 库*/
if ( error )
{
printf("FT_Init_FreeType error!\n");
return -1;
}
error = FT_New_Face( library, argv[1], 0, &face ); /* 打开一个字体文件 */
if ( error )
{
printf("FT_New_Face error!\n");
return -1;
}
slot = face->glyph;
FT_Set_Pixel_Sizes(face, 24, 0); /* 设置字体大小: 24*24 Pixel */

/* 确定起始点坐标:
* lcd_x = 0;
* lcd_y = 24;
* 笛卡尔坐标系:
* x = lcd_x = 0;
* y = var.yres - lcd_y = var.yres - 24;
*/
pen.x = 0 * 64;
pen.y = (var.yres - 24) * 64;

for(i = 0; i < wcslen(wstr1); i++)
{
/* 设置矩阵 */
FT_Set_Transform( face, 0, &pen ); /* 字符保持不变换 */

/* 根据编码值加载glyph到slot */
error = FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER );
if ( error )
{
printf("FT_Load_Char error!\n");
return -1;
}

/* slot在FT_Load_Char()中自动装填,
* slot->bitmap为当前字符 wstr1[i] 的字形点阵位图;
* slot->bitmap_left为当前字符 wstr1[i] 的点阵位图的笛卡尔坐标 x 坐标;
* slot->bitmap_top为当前字符 wstr1[i] 的点阵位图的笛卡尔坐标 y 坐标;
* var.yres - slot->bitmap_top 为当前字符 wstr1[i] 的点阵位图的LCD坐标系 y 坐标;
*/
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
var.yres - slot->bitmap_top );
pen.x += slot->advance.x;
}
return 0;


6、代码解析
typedef struct EncodingOpr {
char *name;
/* 处理文件的时候不关心它的头部,头部只是用来表示哪种格式,比如说ANSI编码方式没有头部,
* utf-16be有两个字节的头部,utf-8有3个字节的头部,因此添加该项元素iHeadLen;
*/
int iHeadLen;

/* 链表头,所有支持该编码格式(例如:ascii.c编码文件支持的字体文件有ascii_font.c, gbk.c)的字体操作结构体,
* 构成一个链表,链表头为 ptFontOprSupportedHead;
*/
PT_FontOpr ptFontOprSupportedHead;

/* 传入文件的头部的buf,判断是否支持 */
int (*isSupport)(unsigned char *pucBufHead);
/* 用该函数得到字符的编码值,再根据编码值从ptFontOprSupportedHead链表中得到字符的点阵;
*/
int (*GetCodeFrmBuf)(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
struct EncodingOpr *ptNext;
}T_EncodingOpr, *PT_EncodingOpr;
注1:wchar_t chinese_str[] = L"繁"; /* 定义一个矢量字体 */
输入是国标码,但是在字符串chinese_str[]的数组内存里面存储的是宽字符的类型,
宽字符的类型里面即Unicode码。
注2:字符编码的演变:
1代:ASCII编码方式: 用1个字节表示一个字符; 文件没有头部,iHeadLen=0。
2代:GBK编码方式: 用2个字节表示一个字符/汉字;
3代:Unicode编码方式:用3个字节表示一个Unicode码;
Unicode 编码共有三种具体实现,分别为utf-8,utf-16,utf-32,其中utf-8占用一到四个字节,utf-16占用二或四个字节,utf-32占用四个字节。
所有的utf-8编码文件的前三字节都是0xEF, 0xBB, 0xBF,没哟例外!
所有的utf-16LE编码文件的前2字节都是0xFF, 0xFE,没哟例外!
所有的utf-16BE编码文件的前2字节都是0xFE, 0xFF,没哟例外!
例1:static T_EncodingOpr g_tUtf8EncodingOpr = { //分配设置一个结构体;
.name = "utf-8",
.iHeadLen = 3, //utf-8文件里面,前面3字节是它的头部,用来表示它是utf-8,
.isSupport = isUtf8Coding,
.GetCodeFrmBuf = Utf8GetCodeFrmBuf,
};
static int isUtf8Coding(unsigned char *pucBufHead)
{
/* 所有的utf-8编码文件的前三字节都是0xEF, 0xBB, 0xBF,没哟例外!
*/
const char aStrUtf8[] = {0xEF, 0xBB, 0xBF, 0};
if (strncmp((const char*)pucBufHead, aStrUtf8, 3) == 0)
{
/* UTF-8 */
return 1;
}
else
{
return 0;
}
}
static T_EncodingOpr g_tUtf16leEncodingOpr = {
.name = "utf-16le",
.iHeadLen = 2,
.isSupport = isUtf16leCoding,
.GetCodeFrmBuf = Utf16leGetCodeFrmBuf,
};
static int isUtf16leCoding(unsigned char *pucBufHead)
{ const char aStrUtf16le[] = {0xFF, 0xFE, 0};
if (strncmp((const char *)pucBufHead, aStrUtf16le, 2) == 0)
{
/* UTF-16 little endian */
return 1;
}
else
return 0;
}

static T_EncodingOpr g_tUtf16beEncodingOpr = {
.name = "utf-16be",
.iHeadLen = 2,
.isSupport = isUtf16beCoding,
.GetCodeFrmBuf = Utf16beGetCodeFrmBuf,
};
static int isUtf16beCoding(unsigned char *pucBufHead)
{ const char aStrUtf16be[] = {0xFE, 0xFF, 0};
if (strncmp((const char*)pucBufHead, aStrUtf16be, 2) == 0)
{
/* UTF-16 big endian */
return 1;
}
else
return 0;
}

UTF-8编码: 内容:A中————EF BB BF 41 E4 B8 AD,其中EF BB BF 为UTF-8文件头部,0x41为A, E4 B8 AD为“中”;
UTF-16LE编码:内容:A中————FF FE 41 00 2D 4E, 其中FF FE 为UTF-16LE文件头部,0x41为A, 00 2D 4E为“中”;

 

二、错误及解析
1、关于头指针、头结点、首节点、尾结点的存在与意义
答:<1>郝斌老师的程序有头指针、头结点、首节点、尾结点。头结点中的数据域无数据,指针域为头指针。
<2>其他程序,如韦东山的程序: 只有头指针、首节点、尾结点,而无头结点。头指针指向首节点。

 

printf("iPenX = %d, iPenY = %d, bitmap_left = %d, bitmap_top = %d, width = %d, rows = %d\n",
iPenX, iPenY, g_tSlot->bitmap_left, g_tSlot->bitmap_top, g_tSlot->bitmap.width, g_tSlot->bitmap.rows);

 

推荐阅读