| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 4045 人关注过本帖
标题:[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 ...
只看楼主 加入收藏
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
结帖率:100%
收藏
 问题点数:0 回复次数:13 
[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显示位图 - 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]]
搜索更多相关主题的帖子: 位图 图形 艺术 编写 开发 
2008-02-14 14:52
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
收藏
得分:0 
消灭0恢复,自顶~~!

个人网站 -  http://.h001.
2008-02-15 08:45
一笔苍穹
Rank: 1
等 级:新手上路
帖 子:640
专家分:0
注 册:2006-5-25
收藏
得分:0 
大家都回去过年了,我也是才到公司,终于上网了。
辛苦了,啥也不说了,项一个先!!啥时候能写完啊,你这跟撇条一样一次撇一点也忒不过瘾了
2008-02-15 10:57
xianshizhe111
Rank: 1
等 级:新手上路
帖 子:1451
专家分:0
注 册:2007-12-8
收藏
得分:0 
太那啥了(顶顶了).
2008-02-15 14:59
ba_wang_mao
Rank: 2
来 自:成都理工大学
等 级:论坛游民
帖 子:297
专家分:27
注 册:2006-11-7
收藏
得分:0 
太棒了,受益非浅,谢谢,继续支持。

多年以来还在MSDOS、单片机下搞嵌入式编程,对WINDOWS编程一窍不通,很想了解WINDOWS下病毒编程技术。
2008-02-16 09:53
skrot
Rank: 1
等 级:新手上路
帖 子:55
专家分:0
注 册:2008-2-16
收藏
得分:0 
没什么说的了,
谢谢

心中明了路向何方,前路自有一番风光。
     skroty@.cn
2008-02-17 21:58
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
收藏
得分:0 
多谢各位支持

个人网站 -  http://.h001.
2008-02-17 22:33
ba_wang_mao
Rank: 2
来 自:成都理工大学
等 级:论坛游民
帖 子:297
专家分:27
注 册:2006-11-7
收藏
得分: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);
    }
}

多年以来还在MSDOS、单片机下搞嵌入式编程,对WINDOWS编程一窍不通,很想了解WINDOWS下病毒编程技术。
2008-02-18 09:01
jig
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
帖 子:530
专家分:242
注 册:2005-12-27
收藏
得分:0 
没错,用movedata将可以大大的提升性能,但本文章只是说明问题就可以了。

后面我们将讨论到movedata的使用。使用movedata方法将会在架构上做处理,以实现合理的结构下最优的速度。

个人网站 -  http://.h001.
2008-02-18 10:21
hoodlum1980
Rank: 2
来 自:浙江大学
等 级:论坛游民
威 望:2
帖 子:289
专家分:23
注 册:2008-2-24
收藏
得分:0 
请问这个是楼主原创吗?这个开发包非常好啊,呵呵~~~~~,我决定收藏了~~~~[bc03]
2008-02-25 16:19
快速回复:[原创]如何编写自己的图形开发包 — 第四章 艺术!这就是艺术! — 显 ...
数据加载中...
 
   



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

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