[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 - 4.2
此文出至 www. 作者:孙靖[bo]第四章
艺术!这就是艺术! — 显示位图[/bo]
[bo]4.2 高彩位图的显示
A 首先,我们来显示一张16B的BMP图片。图片展示如下:[/bo]
图4.5 即将被现实16B位图效果
很绚吧,其实有更绚的图片,只是为了和24B比较,所以找了这张,不过此图比起那256色的的确绚丽了很多。
前面说过,16B是以2个字节来表示一个像素点的颜色。(0-65535)上图是以5:6:5的模式(即RGB为5:6:5)。由于不存在调色扳一说,所以实际的图像数据是从第55个字节开始的。下面我们就来看下应该怎样去显示这幅16B的图片。
代码:查看Chap04-2\main1
typedef struct tagRGBBMP /* 用于读取16B实际图像数据 */
{
UINT16 r:5;
UINT16 g:6;
UINT16 b:5;
} RGBBMP16;
一个用于读取像素信息的结构,看我们直接用很自动的方式将r:g:b按5:6:5的给分配好。(这样做有个好处就是你不必再自己去处理数据,直接将数据从文件里读取出来就OK)
void Dot16(INT16 x, INT16 y, UINT16 color); /* 16B模式画点函数 */
void Dot16(INT16 x, INT16 y, UINT16 color)
{
static UINT8 OldPage = 0;
UINT32 PageAll1, PageAll2;
UINT8 NewPage;
if (x > -1 && x < COLS && y > -1 && y < ROWS)
{
PageAll1 = PageAll2 = (UINT32)y*((UINT32)COLS*2)+((UINT32)x*2);
NewPage = PageAll1 >> 16;
if (NewPage != OldPage)
{
OldPage = NewPage;
SelectPage(NewPage);
}
*((UINT16 far *)(VideoBuffer + PageAll2)) = color;
}
}
重新改造的画点函数,大家请注意:
PageAll1 = PageAll2 = (UINT32)y*((UINT32)COLS*2)+((UINT32)x*2);
……
*((UINT16 far *)(VideoBuffer + PageAll2)) = color;
拿与256色中的画点函数比较一下?仔细体会,相信你会有很重要的收获。你也可以试着自己适当修改他们看看导致的效果。
int main()
{
INT16 fp;
INT16 i, j;
UINT32 Width, Height, Linebytes;
/* 比较之前的定义 */
UINT16 Buffer[640];
RGBBMP16 RGBBuffer[640];
/*----------------*/
struct REGPACK reg;
InitSVGA(); /* 图形初始化开始 */
SetSVGAMode(TRY640X480X16B); /* 实现图形模式 */
if ((fp = open("16b.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);
if (Width%4) /* 拼凑4字节倍数 */
{
Linebytes = Width*2+(4-(Width*2)%4);
}
else
{
Linebytes = Width*2;
}
/*----------由于是高彩-16B,不存在设置调色板一说----------*/
lseek(fp, 54L, SEEK_SET); /* 跳到图像数据区 */
for (i = Height; i > -1; i--)
{
/*read(fp, RGBBuffer, Linebytes); 一次读取一行 */
/*memcpy(Buffer, RGBBuffer, Linebytes);*/
read(fp, Buffer, Linebytes);
for (j = 0; j < Width; j++)
{
Dot16(j, i, Buffer[j]);
}
}
close(fp);
getch();
ExitSVGA(); /* 恢复原始的模式 */
}
首先,我们要关注的是的:
/*read(fp, RGBBuffer, Linebytes); 一次读取一行 */
/*memcpy(Buffer, RGBBuffer, Linebytes);*/
read(fp, Buffer, Linebytes);
其实你直接使用
Read(fp, RGBBuffer, Linebytes);
memcpy(Buffer, RGBBuffer, Linebytes);
的效果相当于
read(fp, Buffer, Linebytes);
这里这样写,是为了您能自己去体会RGB为5:6:5的含义。
其次,我们要关注的是
if (Width%4) /* 拼凑4字节倍数 */
{
Linebytes = Width*2+(4-(Width*2)%4);
}
else
{
Linebytes = Width*2;
}
看这个对齐过程。因为是16B位图,2个字节表示一个像素,所以都“*2”。(我们的BMP文件中一行就储存了这么多个字节。不清楚?回过头看看256色中的,明白了再回来看这个地方就明白啦。仔细体会你还会对数据储存方面有个认识,怎么做才能以最小的代价实现我们要的效果)
这样就OK啦,其实代码和256色的比较是十分类似的。您只要稍加用心琢磨一下就可以明白。
图4.5_2 16B位图现实效果
[bo]B 接下来,我们来显示一张24B的BMP图片。图片展示如下:[/bo]
图4.6 即将被现实24B位图效果
仔细观看此张图片和上面的16B位图,是否更加细腻柔和。(观察边缘和渐变色区域)所以要能现实出一张这样华丽的图片是多么让人激动的事。(我是很激动,你激动吗,呵呵抒情到此……)
既然我们前面有实现256色和16B的经验了,那么要完成24B位图的显示当然也就大同小异啦。放代码:
代码:查看Chap04-2\main2
typedef struct tagRGBBMP /* 用于读取24B实际图像数据 */
{
UINT8 r;
UINT8 g;
UINT8 b;
} RGBBMP24;
和16B的类似,看这个结构。与上面的对比一下,我们已经没有按位取,而是直接的3个字节。
再看看画点函数:
void Dot24(INT16 x, INT16 y, UINT32 *color); /* 24B模式画点函数 */
void Dot24(INT16 x, INT16 y, UINT32 *color)
{
static UINT8 OldPage = 0;
UINT32 PageAll1, PageAll2;
UINT8 NewPage;
RGBBMP24 k;
if (x > -1 && x < COLS && y > -1 && y < ROWS)
{
PageAll1 = PageAll2 = (UINT32)y*((UINT32)COLS*4)+((UINT32)x*4);
NewPage = PageAll1 >> 16;
if (NewPage != OldPage)
{
OldPage = NewPage;
SelectPage(NewPage);
}
*((UINT32 far *)(VideoBuffer + PageAll2)) = *color;
}
}
同样关注:
PageAll1 = PageAll2 = (UINT32)y*((UINT32)COLS*4)+((UINT32)x*4);
……
*((UINT32 far *)(VideoBuffer + PageAll2)) = *color;
回过头看看16B和256色有什么异同?好好体会“数据类型”的基础知识。很多时候我们最以为自己熟悉简单的东西,当你仔细推敲的时候才发现其实你并不是真的了解。
再就是主代码啦:
int main()
{
INT16 fp;
INT16 i, j;
UINT32 Width, Height, Linebytes;
/* 比较之前的定义 */
RGBBMP24 RGBBuffer[640];
/*----------------*/
struct REGPACK reg;
InitSVGA(); /* 图形初始化开始 */
SetSVGAMode(TRY640X480X24B); /* 实现图形模式 */
if ((fp = open("24b.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);
if (Width%4) /* 拼凑4字节倍数 */
{
Linebytes = Width*3+(4-(Width*3)%4);
}
else
{
Linebytes = Width*3;
}
/*----------由于是高彩-24B,不存在设置调色板一说----------*/
lseek(fp, 54L, SEEK_SET); /* 跳到图像数据区 */
for (i = Height; i > -1; i--)
{
read(fp, RGBBuffer, Linebytes); /* 一次读取一行 */
for (j = 0; j < Width; j++)
{
Dot24(j, i, (UINT32 *)&RGBBuffer[j]);
}
}
close(fp);
getch();
ExitSVGA(); /* 恢复原始的模式 */
}
还是一样,关注一下:
if (Width%4) /* 拼凑4字节倍数 */
{
Linebytes = Width*3+(4-(Width*3)%4);
}
else
{
Linebytes = Width*3;
}
怎么样?现在这里是“*3”啦。也许有的朋友就注意到啦,我们画点函数里明明是“*4”,怎么这里是“*3”呢。
我们要明确,画点函数里面的“*4”可是对内存写数据,你看过我们编程里面哪个独立的标准数据类型是像3字节这样的奇数的吗?当然没有,机器全是以“偶数来处理数据”的。(好好体会这就话)在这里,不错!24B的位图当然只要3个字节就可以表示一个像素啦,可为什么我们的画点函数采用了4字节呢?这是为了快!具体为什么?去了解机器运作原理。
而我们的“/* 拼凑4字节倍数 */”的确是只“*3”。这又是为什么?呵呵,我们拼凑字节对齐,是因为我们的BMP文件中一行就储存了这么多个字节的数据。(记得16B的介绍我也是这么说的吧)那么文件储存数据当然是体积越少越好,既然3字节其实就已经够用,那当然不用再消耗更多的磁盘资源。(拿256色和16B与24B的仔细对比,好好体会,所以我总觉得通过图形编程会让你发散型的了解学习更广的知识)
看效果图:
图4.6_2 24B位图现实效果
呵呵,大功告成。我们完成了高彩位图的显示。也许本节介绍的比较粗略,但我想我已经说到点子上啦,这些知识足够您自己去做更多更具吸引力的尝试啦。而且本节的内容并不会作为我们图形开发包的一部分,所以就介绍到这里。
我们下面将开始逐步在架构的角度来改良我们已有的成果,使我们的“图形开发包”更趋向于规范!
对应资料:
Chap04-2.rar
(577.11 KB)
[[it] 本帖最后由 jig 于 2008-2-14 14:58 编辑 [/it]]