Uboot实现文字纵向输出的原理和实现

来源:互联网 发布:javascript innerhtml 编辑:程序博客网 时间:2024/06/05 03:43

http://blog.chinaunix.net/uid-28623414-id-3704544.html

一、相关数据结构:

1.#define CONSOLE_FG_COL 0xa0

#define CONSOLE_BG_COL 0x00

这两个宏是用来生成前景色和背景色的参考值。

2.static u32 eorx, fgx, bgx;

fgx = (((CONSOLE_FG_COL >> 3) << 27) |

((CONSOLE_FG_COL >> 2) << 21) | ((CONSOLE_FG_COL >> 3) << 16) |

((CONSOLE_FG_COL >> 3) << 11) | ((CONSOLE_FG_COL >> 2) << 5) |

(CONSOLE_FG_COL >> 3));

前景色,最终为:

27 21 16 11 5 0

10100 101000 10100 10101 101000 10100

其中,红色和蓝色分量均为10100B,极限值为11111B,绿色分量为101000B,极限值为111111B

bgx = (((CONSOLE_BG_COL >> 3) << 27) |

((CONSOLE_BG_COL >> 2) << 21) | ((CONSOLE_BG_COL >> 3) << 16) |

((CONSOLE_BG_COL >> 3) << 11) | ((CONSOLE_BG_COL >> 2) << 5) |

(CONSOLE_BG_COL >> 3));

背景色,最终为:

27 21 16 11 5 0

00000 000000 00000 00000 000000 00000

其中定义eorx = fgx ^ bgx,用来实现前景背景的混合输出。最终,它的值还是等于fgx的值。因为0和任何数做异或运算,此数的值不变。到后面,我们可以看到背景色是如何和前景色相互配合使用的。

3.static const int video_font_draw_table16[] = {

0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff };

此结构为输出颜色索引表,用来实现字库中有效像素数据(2位二进制数)到实际像素数据(RGB565)的映射。

4.#define SHORTSWAP32(x) ((((x) & 0x000000ff) << 8) | (((x) & 0x0000ff00) >> 8)|\

(((x) & 0x00ff0000) << 8) | (((x) & 0xff000000) >> 8) )

此宏用来交换32位数的高低16位中的高低八位的顺序。

5.#define VIDEO_FONT_CHARS 256

//字符个数,ascii码字符总数为256

#define VIDEO_FONT_WIDTH 8 //字符宽度

#define VIDEO_FONT_HEIGHT 16 //字符高度

#define VIDEO_FONT_SIZE (VIDEO_FONT_CHARS * VIDEO_FONT_HEIGHT)

//定义了字库所占的内存空间大小。

6.static unsigned char video_fontdata[VIDEO_FONT_SIZE] = {

/* 0 0x00 '^@' */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

。。。 。。。

/* 65 0x41 'A' */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x10, /* 00010000 */

0x38, /* 00111000 */

0x6c, /* 01101100 */

0xc6, /* 11000110 */

0xc6, /* 11000110 */

0xfe, /* 11111110 */

0xc6, /* 11000110 */

0xc6, /* 11000110 */

0xc6, /* 11000110 */

0xc6, /* 11000110 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

。。。 。。。

/* 255 0xff '?' */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

0x00, /* 00000000 */

};

此结构为ASCII字符字库,保存了每个字符的行信息(每字符16行,每像素占一行)。

二、Uboot字符显示原理:

首先,Uboot里面实现了一系列ASCII码的点阵字库(816像素,定义在include/video_font.h中,以无符号字符数组形式保存),他们其中的每个字符相关元素的顺序与ASCII码的顺序一致,这样,我们在访问每个字符的时候,就可以通过字符的ASCII值与每个字符编码所占的空间来进行偏移量计算,从而得到相关的数据,字库中每个字符信息都包含十六个数据,且每个数据都是一个八位无符号整数,展开成二进制后,他们每一位都对应了实际显示一个像素,其中为1的位代表亮,为0的位代表暗,这样就完成了一个字符从外形到抽象的数据的转换保存。如:

假设我们要显示的字符是字符'T',则我们可以从字库中,得到如下信息:

/* 84 0x54 'T' */

0x00, /* 0000000*/对应文字像素第18个像素点

0x00, /* 00000000 */对应文字像素第28个像素点

0x7e, /* 01111110 */ … … …

0x7e, /* 01111110 */ … … …

0x5a, /* 01011010 */ … … …

0x18, /* 00011000 */ … … …

0x18, /* 00011000 */ … … …

0x18, /* 00011000 */红色为实际显示时有效的像素点

0x18, /* 00011000 */ … … …

0x18, /* 00011000 */ … … …

0x18, /* 00011000 */ … … …

0x3c, /* 00111100 */ … … …

0x00, /* 00000000 */ … … …

0x00, /* 00000000 */ … … …

0x00, /* 00000000 */ … … …

0x00, /* 00000000 */对应文字像素第168个像素点

它的ASCII码值为84,所以我们可以通过8416为偏移量标来访问到它,在程序中实现为:u8 *cdat = video_fontdata + c * VIDEO_FONT_HEIGHT。其中红色部分就是我们要显示的T字的形式化表达,这样,我们就可以根据这样一个字符数组来将其中的每一位数映射到实际的像素点阵中,从而完成由形式化的数据到实际像素的转换,但在实际的实现中,要根据实际的每像素颜色位数(Bpp)来进行1位到多位的映射,如:0b01-->0x0000ffff。在这里,实际上是完全的颜色映射,即映射后为完全的RGB565最深颜色(0xffff)或最浅颜色(0x0)。

下面,我们通过字符T’的第三行数据0x7e来演示映射的过程:

首先,通过将这个8位二进制数两两分组,我们可以得到:01B,11B,11B,10B四组2位二进制数,然后,通过依次用它们作为数组下标,来查询video_font_draw_table16由于此数组中保存的十六进制数以四四分组,0x0000对应传递进来的0B(表示RGB分量全为最小值),0xffff对应传递进来的1B(表示RGB分量全为最大值),所以01B就映射成了0x0000ffff11B映射成了0xffffffff10B映射成了0xffff0000。也就是将一个2位数映射成了一个32位数,也就是两个像素。即:

27 21 16 11 5 0

00000 000000 00000 fffff ffffff fffff

通过将上面得到的结果与eorx作按位与操作,将0x0000ffff有效像素(0xffff)的全色转换成前景色:

27 21 16 11 5 0

00000 000000 00000 fffff ffffff fffff


27 21 16 11 5 0

eorx10100 101000 10100 10101 101000 10100

结果为:

27 21 16 11 5 0

00000 000000 00000 10101 101000 10100

然后通过与bgx异或,将无效像素(0x0000)保留为背景色,结果为:

27 21 16 11 5 0

00000 000000 00000 10101 101000 10100

最后,将得到的16位像素信息写入framebuffer即可:

((u32 *) dest)[0] = SHORTSWAP32 ((video_font_draw_table16 [bits >> 6] & eorx) ^ bgx);

这里这条语句实际上完成了216位像素的同时写入。

三、横向文字输出原理:

如上图,其中P1...P8等就是我们的实际像素点,如果我们想显示我们自己的字符,那么,实际上就是将我们字库video_fontdata中字符信息数组的相应字符元素都映射并填充到P1P2等像素点上去,我们每个字符对应着16行像素和8列像素的一块区域,我们的framebuffer地址连续,所以,我们可以由P1(R1-C1)地址顺序写下文字的一行信息(R1:P1,R1:P2... R1:P8),至于纵向我们则可以通过计算屏幕一行像素所占空间,然后将地址向后偏移这么多空间,即可获得R1:P1下方像素(R1:P1)的地址,然后继续写入一行,如此往复,直到我们写满16行(R16,因为字符高度是16像素),完成一个字符的写入过程。


C1

C2

C3

C4

C5

C6

C7

C8

C1’

R1

P1

P2

P3

P4

P5

P6

P7

P8

P1

R2

P1

P2

P3

P4

P5

P6

P7

P8

P1

R3

P1

P2

P3

P4

P5

P6

P7

P8

P1

R4

P1

P2

P3

P4

P5

P6

P7

P8

P1

R5

P1

P2

P3

P4

P5

P6

P7

P8

P1

R6

P1

P2

P3

P4

P5

P6

P7

P8

P1

R7

P1

P2

P3

P4

P5

P6

P7

P8

P1

R8

P1

P2

P3

P4

P5

P6

P7

P8

P1

R9

P1

P2

P3

P4

P5

P6

P7

P8

P1

R10

P1

P2

P3

P4

P5

P6

P7

P8

P1

R11

P1

P2

P3

P4

P5

P6

P7

P8

P1

R12

P1

P2

P3

P4

P5

P6

P7

P8

P1

R13

P1

P2

P3

P4

P5

P6

P7

P8

P1

R14

P1

P2

P3

P4

P5

P6

P7

P8

P1

R15

P1

P2

P3

P4

P5

P6

P7

P8

P1

R16

P1

P2

P3

P4

P5

P6

P7

P8

P1

1:单个横向字符像素排列顺序

当我们显示完一个字符后,想显示下一个字符,由于我们横向显示,即显示C1‘右侧的,由于这个位置正好是C1-R1P1像素的framebuffer地址的顺序后续地址,所以我们只要向后跳转字符宽度所占的地址空间即可寻址到C1‘P1地址。在Uboot中,可以用如下方式实现跳转地址的计算:VIDEO_FONT_WIDTH * VIDEO_PIXEL_SIZE,其中 VIDEO_PIXEL_SIZE为每一单位宽度所占的字节数。于是,我们按照这样的方式依次写入,即可完成字符串的输出。

四、纵向文字输出原理:

在上面,我们看到了一个横向文字是按由上而下,一行一行显示文字的,且一行的8个像素点地址连续,只要进行像素的依次写入就可以,在换到下一行时,只要将前一行的首地址加上行偏移像素所占空间数就是下一行的起始地址,但在纵向输出时候,我们如果还按照横向的方法去显示,那么问题会变得复杂,因为,输出每一行,我们都要分别在十六个十六位数中处理一位,即输出C1C16P8P7... ...P1。而我们最方便的办法,是按列输出,也就是P1—P88个像素点整体处理,因为他们的信息在字库中是保存在一个十六位数中的。于是问题就变成如何寻址每一列数据的地址。由上所述,我们可以分别获得C1P1—P8的地址,即R8—R1的行地址,我们在输出每一列时,将首地址偏移某个位置就可以得到C2—C16的存储地址。


C1

C2

C3

C4

C5

C6

C7

C8

C9

C10

C11

C12

C13

C14

C15

C16

R1

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

P8

R2

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

P7

R3

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

P6

R4

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

P5

R5

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

P4

R6

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

P3

R7

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

P2

R8

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

P1

1:单个纵向字符像素排列顺序

于是,我们在设计循环的时候,就可以按照横向字符的高度(16像素,也就是纵向显示时候的宽度)来做外层循环,控制显示列数,内部循环用横向文字宽度(8像素,也就是纵向显示时候的高度),内层分别绘制P1P88个像素点,绘制完毕后将这8个像素点的地址依次加2(因为我们像素是RGB565,占2字节,在程序中,我们把地址转换成无符号十六位整数形,所以,只要将地址加一就可以了,地址的偏移量我们用tmp来替换),直到绘制完所有列。

核心代码修改如下:

1.首先在cfb_console.c中开始位置添加如下宏:

//////////// ADD BY WEI /////////////

//#define DEBUG

#define VIDEO_VSHOW //设置字符纵向显示开关

//////////// END OF ADD /////////////


2.video_drawchars()函数中修改字符输出功能:

#if defined(VIDEO_VSHOW)

case GDF_16BIT_565RGB:

while (count--) //输出所有字符,字符数量为count

{

c = *s; //获得字符指针

int tmp;//列数控制

cdat = video_fontdata + c * VIDEO_FONT_HEIGHT;//获取字符c在字库中位置指针

for (cols = VIDEO_FONT_HEIGHT, dest = dest0,tmp =0;cols--;tmp=VIDEO_FONT_HEIGHT- cols) //输出所有列像素

{

u8 bits = *cdat++;

dest = dest0;//获取当前字符地址

((u16 *) dest)[tmp] = (video_font_draw_table16 [bits >> 6]>>16 & eorx) ^ bgx; //输出第11()像素

((u16 *) (dest - ost))[tmp] = (video_font_draw_table16 [bits >> 6] & eorx) ^ bgx; //输出第21()像素

((u16 *) (dest-2*ost))[tmp] = (video_font_draw_table16 [bits >> 4 & 3]>>16 & eorx) ^ bgx; //输出第31()像素

((u16 *) (dest-3*ost))[tmp] = (video_font_draw_table16 [bits >> 4 & 3] & eorx) ^ bgx; //输出第41()像素

((u16 *) (dest-4*ost))[tmp] = (video_font_draw_table16 [bits >> 2 & 3]>>16 & eorx) ^ bgx; //输出第51()像素

((u16 *) (dest-5*ost))[tmp] = (video_font_draw_table16 [bits >> 2 & 3] & eorx) ^ bgx; //输出第61()像素

((u16 *) (dest-6*ost))[tmp] = (video_font_draw_table16 [bits & 3]>>16 & eorx) ^ bgx; //输出第71()像素

((u16 *) (dest-7*ost))[tmp] = (video_font_draw_table16 [bits & 3] & eorx) ^ bgx; //输出第81()像素

}

dest0 -= VIDEO_FONT_WIDTH * VIDEO_LINE_LEN;//指向下一个字符地址

s++;//指向字符串中下一个要输出的字符

}


#else

//原始代码

case GDF_16BIT_565RGB:

… …

#endif

3.video_logo()函数中添加对纵向显示的支持:

#if defined(VIDEO_VSHOW)

space = (VIDEO_COL_LEN / 2 - VIDEO_INFO_Y) / VIDEO_FONT_WIDTH;

len = strlen(info);

video_drawstring(VIDEO_INFO_X,VIDEO_INFO_Y,(uchar *)info);

if (len > space) {

video_drawchars (VIDEO_INFO_X, VIDEO_INFO_Y,

(uchar *)info, space);

video_drawchars (VIDEO_INFO_X + VIDEO_FONT_HEIGHT,

VIDEO_INFO_Y + VIDEO_FONT_WIDTH,

(uchar *)info + space, len - space);

y_off = 1;

} else

video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *)info);

#else

space = (VIDEO_LINE_LEN / 2 - VIDEO_INFO_X) / VIDEO_FONT_WIDTH;

len = strlen(info);

if (len > space) {

video_drawchars (VIDEO_INFO_X, VIDEO_INFO_Y,

(uchar *)info, space);

video_drawchars (VIDEO_INFO_X + VIDEO_FONT_WIDTH+8,

VIDEO_INFO_Y + VIDEO_FONT_HEIGHT,

(uchar *)info + space, len - space);

y_off = 1;

} else

video_drawstring (VIDEO_INFO_X, VIDEO_INFO_Y, (uchar *)info);

#endif

上面的纵向显示是横向屏幕逆时针旋转90度后的显示状态,如果想实现顺时针转换,需要作另外的代码改动。但原理上是相通的。

0 0
原创粉丝点击