[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 - 4.2
此文出至 www.ds0101.com 作者:孙靖[align=center][bo][size=5]第四章
艺术!这就是艺术! — 显示位图[/size][/bo][/align]
[bo]4.2 高彩位图的显示
A 首先,我们来显示一张16B的BMP图片。图片展示如下:[/bo][attach]32472[/attach]
图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色的比较是十分类似的。您只要稍加用心琢磨一下就可以明白。
[attach]32473[/attach]
图4.5_2 16B位图现实效果
[bo]B 接下来,我们来显示一张24B的BMP图片。图片展示如下:[/bo]
[attach]32474[/attach]
图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的仔细对比,好好体会,所以我总觉得通过图形编程会让你发散型的了解学习更广的知识)
看效果图:
[attach]32475[/attach]
图4.6_2 24B位图现实效果
呵呵,大功告成。我们完成了高彩位图的显示。也许本节介绍的比较粗略,但我想我已经说到点子上啦,这些知识足够您自己去做更多更具吸引力的尝试啦。而且本节的内容并不会作为我们图形开发包的一部分,所以就介绍到这里。
我们下面将开始逐步在架构的角度来改良我们已有的成果,使我们的“图形开发包”更趋向于规范!
对应资料:[attach]32476[/attach]
[[it] 本帖最后由 jig 于 2008-2-14 14:58 编辑 [/it]] 消灭0恢复,自顶~~! 大家都回去过年了,我也是才到公司,终于上网了。
辛苦了,啥也不说了,项一个先!!啥时候能写完啊,你这跟撇条一样一次撇一点也忒不过瘾了 太那啥了(顶顶了). 太棒了,受益非浅,谢谢,继续支持。 没什么说的了,
谢谢 多谢各位支持 jig ,好象16B画点函数有点缺点:以下是我从一本书上摘录下来的
void HLINE(int x1,int y1,int x2,int color)
{
int i;
unsigned char color8 = (unsigned char)color;
unsigned int color16;
long addr = VRAM_GRAPH_800X600X256(x1,y1);
int page;
int far *videoptr = (int far *)0xa0000000L;
int far *video_buffer_w;
unsigned int head_color,tail_color;
color16 = (color8 << 8) + color8;
if (x1&0x0001)
head_color = (color<<8); // 第一点为奇数,不整除,只填写后一点
else
head_color = ((color<<8)|color); // 第一点为偶数,整除,两点都填写
if (x2&0x0001)
tail_color = ((color<<8)|color); // 最后一点为奇数,不整除,两点都填写
else
tail_color = (color<<8); // 最后一点为偶数,整除,只填写前一点
page = (int)(addr >> 16);
if (page == ((addr + (x2-x1+1)) >> 16))
{
video_buffer_w = (int far *)(videoptr + (unsigned)(addr&0xffff));
set_vbe_page(page);
*(video_buffer_w-(x1>>1)) = head_color;//画线首
for (i = (x1>>1)+1; i < (x2>>1) ; i++)
*(video_buffer_w+i)= color16;
*(video_buffer_w-(x2>>1)) = tail_color;//画线尾
}
}
****还有如何将点直接画到XMS中,然后利用如下函数直接拷贝到显存呢?
//////////////////////////////////////////////////////////////////////////快速清除屏幕子程序(将缓冲屏幕中的数据直接复制到可见视频缓存区里面去)
//由于分辨率为800*600*256色(8位深度),因此共需800*600/65535=8页=512K
////////////////////////////////////////////////////////////////////////void _Cdecl GuiQuickClsScreen(COLOR Color)
{
unsigned int i,j;
unsigned char VDCBuf[800];
unsigned int dest_off = 0;
memset(VDCBuf,Color,800);
for (j = 0 ; j < 8 ; j++)
{
set_vbe_page(j);
dest_off = 0;
for (i = 0 ; i < 81 ; i++)
{// 由于显卡每页64K,因此先送64800个像素
movedata(FP_SEG(VDCBuf),FP_OFF(VDCBuf),0xa000,dest_off,800);
dest_off += 800;
}// 将每页余下的736个像素单独送
movedata(FP_SEG(VDCBuf),FP_OFF(VDCBuf),0xa000,dest_off,736);
}
} 没错,用movedata将可以大大的提升性能,但本文章只是说明问题就可以了。
后面我们将讨论到movedata的使用。使用movedata方法将会在架构上做处理,以实现合理的结构下最优的速度。 请问这个是楼主原创吗?这个开发包非常好啊,呵呵~~~~~,我决定收藏了~~~~[bc03] 我现在还看不懂。 好贴,特别崇拜像楼主和一笔苍穹这样牛13的人物!
页:
[1]
