| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2685 人关注过本帖
标题:[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 ...
只看楼主 加入收藏
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
结帖率:100%
收藏
 问题点数:0 回复次数:5 
[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 - 4.1
此文出至 [url]www.[/url]   作者:孙靖

[bold]第四章
艺术!这就是艺术! — 显示位图[/bold]

[bold]4.1 显示一张256色的位图[/bold]
    艺术?没错,我们下面要当当艺术家,(当然,不是行为艺术哦!)显示一张位图。为什么要显示位图?因为,你真以为就用一个画点函数就可以自己一个点一个点的画出绚丽的图案?那是扯淡的。比较绚的图当然要由我们美工哥哥姐姐们先设计好,我们通过程序来显示。只有这样比复杂的游戏才能得以完成。
    我们首先来讨论一下要显示一幅位图需要事先做写什么。我们现在的情况是,已经处于640X480X256的图形模式下,并有一个比较完善的画点函数,那么我们能显示的位图应该就要也是256色的,最好分辨率也是640X480。再有呢,既然是显示一幅位图(*.bmp)那么实质上就是正确读取位图文件的信息,然后将其显示出来就可以了。
    现在问题清晰啦,我们最主要的难点是要弄清楚位图(*.bmp)的具体格式,以便我们能正确的去读取。可我并我打算给各位把BMP格式的全部细节全部给解释清楚,我也不确定我有这个能力。大家要想详细了解这个知识可以到网络上去搜索,应该有一大把。下面我们就从实践开始出发,开始我们的艺术历程。
    我们先假定我们有一张640X480X256的图片,您可以在Chap04下查看mnls.bmp,没错,我们要显示的就是她。什么?你怎么知道这张图片是640X480X256的?最简单的方法,鼠标右键点击此mnls.bmp文件图标,点“属性”。怎么样?看到是640X480吧?而且“位深度 8”,眼熟吧。指的就是8位色深,共256种颜色。当然因为是对文件操作,我就假定您已经会C语言的文件操作。
    先来看看我们要显示的图片
图片附件: 游客没有浏览图片的权限,请 登录注册

4.1 我们要显示的图片

    嘿嘿,够艺术吧。这就是我们要显示的图片啦。
    1.为了能让程序不那么刻板,我们首先还是要通过读取文件信息,来确定我们即将显示的这张图片的高,宽。这个信息呢,在文件的第19-26字节。其中前4字节表示宽,后4字节表示高。

代码:查看Chap04-mian1
if ((fp = open("mnls.bmp", O_RDONLY|O_BINARY)) == -1)    /* 打开文件 */
{
     printf("can't open the file!");
getch();
return 0;
}
lseek(fp, 18L, SEEK_SET);                               /* 读取文件宽高     */
read(fp, &Width, 4);
read(fp, &Height, 4);

我强烈推荐用open()函数打开文件。因为他没有使用文件缓存,可以最快的为你实现文件读取(虽然他不那么安全)我们先将文件指针移动到第18个字节处lseek(fp, 18L, SEEK_SET);然后就是两句read()函数获取了文件的高和宽。

2.接下来,我们就去读取我们最想要的图像数据。并把他显示出来。大家要清楚的是,8色深的BMP图,是1个字节表示了一个像素的颜色值,他基本上就是原原本本的将一幅图像给储存了起来,(这也是为什么BMP格式的图片体积比较大的原因)然而也不完全是这样,他的宽度总是以4倍字节存储的,比如有个图片是637的宽,那么他的文件中每行却是有640个字节,为什么要这样呢?以后你了解了其他色深格式的BMP文件你就明白了,现在先记住。还有,为了能尽快将信息读取完毕,我们将一次读取一行数据,并且我们所需要的信息是从第1079字节开始的。那有朋友会问,要尽可能快我们可以一次把信息全读出来啊,这样只要读文件一次。呵呵,我也想啊,可这是在DOS下,我们不可能去开辟那么一大块的内存空间来储存我们的位图信息。(算算640X480是多少字节?)

代码:查看Chap04-mian1
if (Width%4)                    /* 拼凑4字节倍数    */
{
      Linebytes = Width+(4-Width%4);
}
else
{
     Linebytes = Width;
}

lseek(fp, 1078L, SEEK_SET);     /* 跳到图像数据区        */
for (i = 0; i < Height; i++)
{
     read(fp, Buffer, Linebytes);    /* 一次读取一行             */
     for (j = 0; j < Width; j++)
        Dot(j, i, Buffer[j]);
}
close(fp);

简单吧,这样就可以了。注意,按照我们上面所强调的,我们拼凑了4字节倍数Linebytes,在后面用来每次读取一行数据read(fp, Buffer, Linebytes);。并且,我们在读取数据前将文件指针移动到了图像数据区首:lseek(fp, 1078L, SEEK_SET);,效果如图:
图片附件: 游客没有浏览图片的权限,请 登录注册

图4.2 初步位图显示效果

    我的天,失败了。这是个什么东西啊?怎么和我们的图4.1差了这么多?不过等等,粗略的看还真有个人的轮廓,只是怎么是倒的?我们再改改,先把人正过来看看?

代码:查看Chap04-mian2(基本和Chap04-main1一样,给出不用部分)
……
for (i = Height; i > -1; i--)
……

我们更改打印的顺序将上下交换就OK,效果如图4.3:
图片附件: 游客没有浏览图片的权限,请 登录注册

图4.3 倒置后的效果

    大家可以看到,虽然还是花花的一篇,但我们可以清楚的从轮廓上看出来,的确是图4.1。只是为什么是花的呢?
    原来,256色存在一个“调色板”的问题。当初这个知识点可把我坑苦了。
大家试想一下,我们的8色深模式只有0-255来表示256种颜色。那么现实世界的颜色可以说是无穷无尽的,如果要让计算机来完全表现这样多的颜色值那是不可能的。(那你定义0-N,来分别表示颜色,那N要多少?)所以我们的机器在实现颜色的时候还是参照我们现实世界的规律,采用RGB三原色混合的形式来产生颜色。(从这个机理出发,我们的确可以让我们的显示器发出任何我们想要的颜色)也就是说,在高彩下我们的BMP文件其实是要保存一个RGB的组合才能纪录下更多的颜色值。还记得第二章图2.2下面,我特别强调大家记住的那段话吗?比如在16位色深模式下,在显存中是以两字节来纪录一个像素的颜色值的,所以你还能将我们的VideoBuffer一个字节对应一个像素来使用吗?虽然,定义为(UINT8 *)也没错,只是在使用的时候就不能再是如:VideoBuffer[1] = 256;因为这样其实是溢出了,实际写入的是0。而如果你将其定义为(UINT16 *)那VideoBuffer[1] = 300;就可以正常使用。
这里再拓展的讨论一下,理论上我们是可以让我们的显示器显示任何颜色值,可在我们的位图中,是离散的纪录了有限个点的有限种颜色。因为采用的RGB模式(每个分量一个字节)如R:0-255,并且是整数形式,所以其组合出来的颜色还是有限的。(而且RGB还可能是按5:6:5的形式安排,即R用5位,G用6位,B用5位,这样总共就使用了16位,可表示65536种颜色,虽然在BMP文件中的确是储存了3个字节)此处的知识,大家可能要找到BMP文件格式详细研究才能完全掌握。
既然我们的机器真实的表现颜色是要RGB组合才能OK,那我们这256色模式,是一个字节表示一种颜色啊,那怎么办?
终于绕回来啦。这就是我们前辈们的聪明才智所在……
我们再往下想一层。的确,我给VideoBuffer[0] = 0;写入了一个0,他表现出来是黑色。那凭什么他要是黑色?谁规定的?总共才0-255表示的256种颜色,为什么0就是黑色,为什么0不可以是红色呢?
要是你能有以上这个疑问,那离明白答案就不远啦。既然我们的机器实质上是可以发出任何颜色光(由RGB组合嘛),而我们现在只用256种颜色。那这256种颜色该是哪些呢?其实,这256种颜色就需要你自己给定啦。
说到这,再回想前面说的“调色板”,是不是很形象?我们自己指定0-255这256个数字分别去对应256种颜色。就像画家那大大的调色板里,每个格子里挤一点颜料,在使用的时候直接在对应的格子里提取颜料去画就是了。
    所以,在8色深的BMP文件里,其实还有一组调色板信息。(回头想想,在19字节处,我们读取了宽,高。在1079字节处才是真正的图像数据区,那之间空了这么多肯定是用的啊)我们看到图4.3花花的一片,就是因为没有正确设置这张图片的调色板信息,而直接采用了系统默认的调色板。
下面我们就来进行我们调色板配制工作,我们的配制信息是从第55字节开始的,共有256组信息,每组信息4个字节,其中前三字节RGB信息,最后一个是空字节。我们制造一个结构以便方便的读取此信息。

代码:Chap04-mian3(与Chap04-mian2类似,只增加了若干代码)
typedef struct tagRGBQUAD     /* 用于读取调色板信息                   */
{
    UINT8   r;
    UINT8   g;
    UINT8   b;
    UINT8   reserved;
} RGBQUAD;

……
/*----------以下为设置调色板信息----------*/
lseek(fp, 54L, SEEK_SET);
if (read(fp, (char *)bmiColors, 1024) != 1024)  /* 读当前图片调色板数据     */
{
printf("Can't get palette !\n");
bioskey(0);
exit(0);
}

for (i = 0, j = 0; i < 256; ++i, j +=3) /* 拼凑信息 */
{
Buffer[j] = bmiColors[i].b >> 2;
Buffer[j + 1] = bmiColors[i].g >> 2;
Buffer[j + 2] = bmiColors[i].r >> 2;
}
reg.r_ax = 0x1012;
reg.r_bx = 0;
reg.r_cx = 256;
reg.r_es = FP_SEG(Buffer);
reg.r_dx = FP_OFF(Buffer);
intr(0x10, &reg);                       /* 使用中断完成设置 */
……
    我们可以看到:
1.我们构造了新数据类型RGBQUAD
2.然后我们将文件指针移动到调色板信息处,lseek(fp, 54L, SEEK_SET);
3.我们获取信息后,在一个for循环中做了若干数据操作:
Buffer[j] = bmiColors[i].b >> 2;
Buffer[j + 1] = bmiColors[i].g >> 2;
Buffer[j + 2] = bmiColors[i].r >> 2;
还记得前文中括弧中,说了RGB虽然是用了3字节存储,当他可能只是用到一字节中的某几位,这里就是这样一个处理过程。
4.我们将处理好的Buffer的地址写给了reg的成员,并调用了一个中断。为什么这么做?这就是设置工作,记住就是啦。
    
    到此我们的调色板设置工作就完成了,我们再来看看其效果。
图片附件: 游客没有浏览图片的权限,请 登录注册

图4.4 设置调色板后的效果
    看到吧,很激动吧?再仔细研读一下Chap04-mian3的代码,不用多久你就会觉得,原来显示一张位图也不用很复杂。
    最后,我们还要有若干问题要思考一下:
    1.既然8色深模式要设置调色板,那不是我每显示一张图片都要设置一次?
    如果,你要显示的图片的调色板不一样,那你当然要重新设置你的调色板。然而,如果你所要显示的所有图片调色板都是一样的,那么你就只需要设置一次调色板。神奇吧,这也就意味着,若你设计的图片都同一调色板,那么经过一次调色板设置后再去现实图片就可以直接读取图像数据,这样做最直接的一个好处就是在显示速度上有了大幅度提高。更让人激动的是,你可以直接更改调色板信息,来改变图片看上去的效果,并且这样做比直接再重新画点要快的多。所以你可以做出比如明暗变化和过渡色变化的效果。
    各位可以去关注一下DOS时代很多经典的游戏都是256色,并充分利用调色板功能实现一些绚烂的特效果。
    2.在高彩模式下,是否能做出诸如色彩渐变等效果?
    当然是可以的,只是在256色下可以通过直接更改调色板信息来快速实现。而在高彩模式下,就必须重新去绘制点来实现。这在以前是不可想象的,因为这样的话会奇慢无比,而现在却是家常便饭。正是由于现在硬件性能的大幅度提升高彩模式才得以变的常见起来。想想看嘛,我们3D那绚烂逼真的效果不就是以能在高彩模式下快速重绘为基础的吗?
    3.既然高彩那么好用优秀,为什么还要做256色的?
    就本文来说,以256色来讲解可以更简单的说明问题,还有可以彻底的排除朋友们的疑惑。我们从256色出发再去做显示高彩是件顺其自然的事情。而如果反过来讲就不一定了,各位可能会被调色板这个东东给迷惑住。还有就是,实现个256色的图形开发包相对容易,实现出来的效果也更快。
    而对我们工业上来说,这是个历史遗留问题。我想在现今没有哪家公司还在为加速这个256色模式而做改进。而为了向前兼容性,256色也一直保留下来。诸如此类的很多现象就不说了,应该很多。

    这节,我们做了怎么显示一幅256色的图片的试验。那下一节,我们将做为扩展来尝试一下高彩BMP图片的显示。(但此次高彩位图先是的尝试只做个试验,并不会成为我们将完成的图形开发包的一部分)


配套资料下载:
Chap04.rar (238.94 KB)
搜索更多相关主题的帖子: 位图 艺术 图形 编写 孙靖 
2008-01-24 14:25
一笔苍穹
Rank: 1
等 级:新手上路
帖 子:640
专家分:0
注 册:2006-5-25
收藏
得分:0 
呵呵,渐入佳境了,Come on!
2008-01-24 14:39
ba_wang_mao
Rank: 2
来 自:成都理工大学
等 级:论坛游民
帖 子:297
专家分:27
注 册:2006-11-7
收藏
得分:0 
呵呵,渐入佳境了,Come on!加油!jig

多年以来还在MSDOS、单片机下搞嵌入式编程,对WINDOWS编程一窍不通,很想了解WINDOWS下病毒编程技术。
2008-02-01 12:42
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
收藏
得分:0 
呵呵,后面还在写,这些天正愁怎么回去呢。晚上接着发

个人网站 -  http://.h001.
2008-02-03 10:19
sunyboy1983
Rank: 1
等 级:新手上路
帖 子:5
专家分:0
注 册:2008-2-23
收藏
得分:0 
不错不错~以后请继续发~我一定会支持的
2008-03-01 10:08
saberwen
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:4
专家分:100
注 册:2010-11-28
收藏
得分:0 


有空也  来试试看
2011-03-23 21:07
快速回复:[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显 ...
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.022709 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved