Uboot实现文字纵向输出的原理和实现
2014-01-02 15:33
218 查看
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码的点阵字库(8*16像素,定义在include/video_font.h中,以无符号字符数组形式保存),他们其中的每个字符相关元素的顺序与ASCII码的顺序一致,这样,我们在访问每个字符的时候,就可以通过字符的ASCII值与每个字符编码所占的空间来进行偏移量计算,从而得到相关的数据,字库中每个字符信息都包含十六个数据,且每个数据都是一个八位无符号整数,展开成二进制后,他们每一位都对应了实际显示一个像素,其中为1的位代表亮,为0的位代表暗,这样就完成了一个字符从外形到抽象的数据的转换保存。如:假设我们要显示的字符是字符'T',则我们可以从字库中,得到如下信息:
/* 84 0x54 'T' */
0x00, /* 00000000 */对应文字像素第1行8个像素点
0x00, /* 00000000 */对应文字像素第2行8个像素点
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 */对应文字像素第16行8个像素点
它的ASCII码值为84,所以我们可以通过84*16为偏移量标来访问到它,在程序中实现为: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就映射成了0x0000ffff,11B映射成了0xffffffff,10B映射成了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
eorx:10100 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);
这里这条语句实际上完成了2个16位像素的同时写入。
三、横向文字输出原理:
如上图,其中P1...P8等就是我们的实际像素点,如果我们想显示我们自己的字符,那么,实际上就是将我们字库video_fontdata中字符信息数组的相应字符元素都映射并填充到P1,P2等像素点上去,我们每个字符对应着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 |
当我们显示完一个字符后,想显示下一个字符,由于我们横向显示,即显示C1‘右侧的,由于这个位置正好是C1-R1处P1像素的framebuffer地址的顺序后续地址,所以我们只要向后跳转字符宽度所占的地址空间即可寻址到C1‘处P1地址。在Uboot中,可以用如下方式实现跳转地址的计算:VIDEO_FONT_WIDTH
* VIDEO_PIXEL_SIZE,其中 VIDEO_PIXEL_SIZE为每一单位宽度所占的字节数。于是,我们按照这样的方式依次写入,即可完成字符串的输出。
四、纵向文字输出原理:
在上面,我们看到了一个横向文字是按由上而下,一行一行显示文字的,且一行的8个像素点地址连续,只要进行像素的依次写入就可以,在换到下一行时,只要将前一行的首地址加上行偏移像素所占空间数就是下一行的起始地址,但在纵向输出时候,我们如果还按照横向的方法去显示,那么问题会变得复杂,因为,输出每一行,我们都要分别在十六个十六位数中处理一位,即输出C1-C16中P8,P7......P1。而我们最方便的办法,是按列输出,也就是P1—P8的8个像素点整体处理,因为他们的信息在字库中是保存在一个十六位数中的。于是问题就变成如何寻址每一列数据的地址。由上所述,我们可以分别获得C1列P1—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 |
于是,我们在设计循环的时候,就可以按照横向字符的高度(16像素,也就是纵向显示时候的宽度)来做外层循环,控制显示列数,内部循环用横向文字宽度(8像素,也就是纵向显示时候的高度),内层分别绘制P1-P8这8个像素点,绘制完毕后将这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;
//输出第1行1个(列)像素
((u16 *) (dest - ost))[tmp] = (video_font_draw_table16 [bits >> 6] & eorx)
^ bgx; //输出第2行1个(列)像素
((u16 *) (dest-2*ost))[tmp] = (video_font_draw_table16 [bits >> 4 & 3]>>16
& eorx) ^ bgx; //输出第3行1个(列)像素
((u16 *) (dest-3*ost))[tmp] = (video_font_draw_table16 [bits >> 4 & 3] & eorx)
^ bgx; //输出第4行1个(列)像素
((u16 *) (dest-4*ost))[tmp] = (video_font_draw_table16 [bits >> 2 & 3]>>16
& eorx) ^ bgx; //输出第5行1个(列)像素
((u16 *) (dest-5*ost))[tmp] = (video_font_draw_table16 [bits >> 2 & 3] & eorx)
^ bgx; //输出第6行1个(列)像素
((u16 *) (dest-6*ost))[tmp] = (video_font_draw_table16 [bits & 3]>>16 & eorx)
^ bgx; //输出第7行1个(列)像素
((u16 *) (dest-7*ost))[tmp] = (video_font_draw_table16 [bits & 3] & eorx) ^
bgx; //输出第8行1个(列)像素
}
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度后的显示状态,如果想实现顺时针转换,需要作另外的代码改动。但原理上是相通的。
相关文章推荐
- jQuery实现的动态文字变化输出效果示例【附演示与demo源码下载】
- matlab中实现文字和数字和混输出excel
- GDIplus实现带光圈文字输出
- 实现多行文字对齐的原理
- Servlet中Response对象应用1(输出简单文字、实现文件下载)
- Android View的点击事件导致文字颜色变化的实现原理
- 从0移植uboot(五) _实现串口输出
- Linux C 编程 实现彩色文字输出
- python处理计算机辅助设计软件文字信息并实现排序输出
- 编译原理:用bison实现输入二进制数,输出十进制数
- sql server 纵向表横向输出的实现
- 仿多页面滑动切换时背景指示图(如TAB文字下边的白条等)的动画实现原理,例PagerSlidingTabStrip
- Android实现文字垂直滚动、纵向走马灯效果的实现方式汇总
- Android实现OCR文字识别并且转换为Excel、PDF格式输出
- Opencv 输出文字,实现简单水印
- 实现文字由右向左一个一个的变色输出,可有css+div来控制页面输出位置
- android图片涂鸦——旋转与文字功能的实现原理
- Android文字垂直滚动、纵向走马灯的几种实现方式
- Android自定义“图片+文字”控件四种实现方法之一--------Gallery原理(提供源码下载)
- PHP文字转图片功能原理与实现方法分析