正文

游戏编程起源(初学者)Ⅸ2006-02-02 12:22:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/mroske/9939.html

分享到:

第五章 DirectDraw的调色板和象素

简介

今天我们将分别使用调色板和RGB模式来熟悉DirectDraw的基本图形。它们有什么不同呢?如果你曾经在DOS下编程,你可能使用过调色板映射模式。调色板是个颜色查询表,为了绘制象素,你将一个单独的字节写入视频内存,通过这个字节你可以索引到一个拥有各种颜色的链表,这个颜色的链表,或查询表就叫作调色板。而RGB模式是不同的,因为它不需要颜色查询表。在RGB模式下绘制一个象素,你可以直接把红色、绿色和蓝色的值写入视频内存。任何色彩深度高于8位的调色板都可以用RGB模式取代。
编写本章时,我假设你已经读过了前面几章,知道了怎样设置DirectDraw和创建表面。我们将使用DirectX7,它包含了最新的DirectDraw接口。实际上,DirectX 7 中的DirectDraw接口可能是最后的升级版本了!不用担心,未来的版本一定会兼容它的,但是未来可能是一个DirectDrawDirect3D综合的产品,管它那,我们学的不会没有用的。^_^
在开始前我还有最后一件事要提醒你:在我的后续文章中关于调色板的部分可能再也用不到了,所以,如果你对于调色板模式不是很感兴趣,你可以跳过文章的前一部分,从象素格式开始看起。调色板的开发和使用是PC中使用的原始视频系统的内存限制带来的直接后果。现在由于充足的显存使调色板模式几乎废弃不用了。值得保留调色板模式的一个原因是,执行调色板操作可以实现一些有趣的动画效果。不罗嗦了,让我们开始吧!

创建DirectDraw的调色板

当你在色彩深度为8位或低于8位的模式下显示图形时,你必须创建调色板,也就是颜色查询表。更明确的讲,对于DirectX,调色板就是PALETTEENTRY结构。要建立一个调色板,我们要做如下三步:
1
创建颜色查询链表。
2
得到指向IDirectDrawPalette接口的指针。
3
把调色板链接到DirectDraw表面。

我假设我们使用的是8位色彩深度。如果你要用16位或更高位的色彩深度编写游戏,你就不用继续看以下这段疯狂的Windows素材了。总之,8位色彩深度,我们可以有一个256个条目的调色板。所以,创建颜色查询链表,有256个条目在其中:

typedef struct tagPALETTEENTRY { // pe
    BYTE peRed;
    BYTE peGreen;
    BYTE peBlue;
    BYTE peFlags;
} PALETTEENTRY;


头三个参数很明显,分别是红色、绿色和蓝色的强度。每一个取值范围0255BYTE是无符号数据类型。最后一个参数是控制标志,应该设置为PC_NOCOLLAPSE。原因我就不说了。
现在,我们需要把256个条目有秩序的排列好,也就是为了一下能找到,我们为链表设置一个数组,象这样:
PALETTEENTRY palette[256];
Ok
,我们有了数组了,你可以装载颜色了。当我工作在调色板模式下时,通常把颜色存储在一个外部文件里,然后用一些如下的东东装载颜色:

FILE* file_ptr;
int x;

if ((file_ptr = fopen("palette.dat", "rb")) != NULL)
{
    fread(palette, sizeof(PALETTEENTRY), 256, file_ptr);
    fclose(file_ptr);
}

All right
,第一步完成了。现在我们需要得到调色板的接口。交给IDirectDraw7::CreatePalette()函数就好了:

HRESULT CreatePalette(
    DWORD dwFlags,
    LPPALETTEENTRY lpColorTable,
    LPDIRECTDRAWPALETTE FAR *lplpDDPalette,
    IUnknown FAR *pUnkOuter
);

返回类型是HRESULT,你知道它的,所以可以用FAILED()SUCCEEDED()这两个宏检测函数是否调用成功。参数的说明如下:
DWORD dwFlags:描述调色板对象的标志常量。当然,你可以用“|”组合它们:
DDPCAPS_1BIT1位色彩,对应2色调色板。
DDPCAPS_2BIT2位色彩,对应4色调色板。
DDPCAPS_4BIT4位色彩,对应16色调色板。
DDPCAPS_8BIT8为色彩,对应256色调色板。
DDPCAPS_8BITENTRIES:指出引用一个8位色彩索引。就是说,每个颜色条目是它本身的到目的表面8位调色板的索引。这叫作被变址的调色板。它必须同DDPCAPS_1BITDDPCAPS_2BIT,或者DDPCAPS_4BIT合用。乱套吧!^_^
DDPCAPS_ALPHA:每一个PALETTEENTRYpeFlags成员都应该被认为是阿尔发值。用这些标志创建的调色板可以被粘贴在Dierct3D纹理表面,因为DirectDraw本身并不支持阿尔发混合。
DDPCAPS_ALLOW256:允许8位调色板的全部256个条目被使用。通常,0指向黑色,255指向白色。
DDPCAPS_INITIALIZE:指出应该用PALETTEENTRY的数组初始化调色板。
DDPCAPS_PRIMARYSURFACE:调色板将链接到主表面,好快速更改显示颜色。
DDPCAPS_VSYNC:一般画圆时用到它。
大多数情况,你将使用DDPCAPS_8BIT | DDPCAPS_INITIALIZE,如果你刚好想建立一个空的调色板,稍后再设置它,你可以去掉后者,就是DDPCAPS_INITIALIZE。当然,你还可以使用DDPCAPS_ALLOW256,如果你真的想改变这两个常用的条目。
LPPALETTEENTRY lpColorTable:这个指针指向我们创建的查询表,把数组的名称传递给它就好了。
LPDIRECTDRAWPALETTE FAR *lplpDDPalette:这是指向IDirectDrawPalette接口指针的地址。如果函数调用成功,它将被初始化。
IUnkown FAR *pUnkOuter:同以前一样,这总是为COM高级应用准备的。设置为NULL好了。

不是太糟糕吧!现在我们可以建立我们的调色板对象了。最后一步是把调色板链接到一个表面,这只需要一个函数就好了——IDirectDrawSurface7::Setpalette()。它的原形如下:

HRESULT SetPalette(LPDIRECTDRAWPALETTE lpDDPalette);

很简单,是不是?你只要把上一步得到的接口指针传递给它就可以了。那好,让我们把学到的综合到一起,下面我给你一个程序框架,我假设我们已经利用调色板的数组建立了一个索引链表,就像我们上一步做的。该框架是建立DirectDraw调色板来控制颜色,并且把它链接到主表面(当然,主表面是我们事先做好的):

LPDIRECTDRAWPALETTE lpddpal;

// create the palette object
if (FAILED(lpdd7->CreatePalette(DDPCAPS_8BIT | DDPCAPS_INITIALIZE, palette, &lpddpal, NULL)))
{
    // error-handling code here
}

// attach to primary surface
if (FAILED(lpddsPrimary->SetPalette(lpddpal)))
{
    // error-handling code here
}

就是这么简单。一旦你的调色板建立完成,绘制象素部分同RGB模式就没有什么不同了。从此时开始,我将同时介绍RGB模式和调色板模式,在我们真正的显示图象前,我需要告诉你什么是RGB象素格式。

象素格式

象我前面说过的,当你把一个调色板模式的象素写入内存时,你同时分配了一个字节,每个字节表示一个到色彩查询表的索引。在RGB模式下,你只需要把颜色描述值写入内存,但每个颜色需要的字节数都要多于一个字节。字节的多少同色彩的深度相关。对于16-bit色彩,你要为每个象素准备两个字节(16位),以此类推,你可以猜到32-bit色彩是怎么回事了,这些都是很容易理解的。32-bit色彩对于一个象素来说,每一位的字符如下:

AAAA AAAA RRRR RRRR GGGG GGGG BBBB BBBB

“A”
是代表“alpha”(阿尔发),表示一个透明的值,这是为Direct3D准备的。我以前说过,DirectDraw不支持α混合,所以当你为DirectDraw创建32-bit色彩时,把高位都设置为0好了。下一个8位表示红色强度的值,再下一个8位表示绿色,最后8位表示蓝色。
一个32-bit色彩的象素需要32位,所以我们一般用UINT类型来定义相对应的变量类型,这是一个无符号实数类型。通常我用一个宏来把RGB数据转换成正确的象素格式。让我给你看看它的样子,希望这能更好的帮助你理解象素格式:

#define RGB_32BIT(r, g, b) ((r << 16) | (g << 8) | (b))

就象你看到的,这个宏通过位移在相应的位置写入了相应的红、绿、蓝的强度值,并且完全符合正确的象素格式。是不是开始感觉有点儿入门了?要建立一个32-bit的象素,你就可以调用这个宏。红、绿、蓝每一个颜色的强度值都是8位,它们的取值范围都是从0——255。例如建立一个白色的象素,你可以这样:

UINT white_pixel = RGB_32BIT(255, 255, 255);

24-bit
色彩基本相同,道理实际上是一样的,只是24-bit没有关于α的描述,也就是少了α8位。象素格式如下:

RRRR RRRR GGGG GGGG BBBB BBBB

所以红色、绿色、蓝色仍然都分别是8位,这就意味着24-bit色彩和32-bit色彩实际上是有相同颜色深度的,只是32-bit多了个α混合。现在,你一定会想,24-bit32-bit要好,真的是这样吗?否,因为使用24-bit有一些麻烦,事实上没有24-bit的数据类型,在你建立象素时,你不得不分三步写入红、绿、蓝的强度值,而不是象32-bit一次就完成。尽管32-bit色彩需要更多的内存,但在大多数的机器上,它要更快一些。实际上,很多显示卡不支持24-bit色彩模式,因为每一个象素占用3个字节是很不方便的。
现在,轮到16-bit色彩了,它有一点儿小麻烦,因为对于16-bit色彩,不是每一种显示卡都使用相同的象素格式!有两种格式。其中一种,也是比较流行的,红色占据5位,绿色占据6位,蓝色占据剩下的5位。另一种格式是分别都占据5位,剩下的一位,也就是高位不使用,一些老的显示卡都使用这种格式。所以这两种格式看起来是这样的:

565 format: RRRR RGGG GGGB BBBB
555 format: 0RRR RRGG GGGB BBBB

当你工作在16-bit色彩深度下,你首先需要检测显示卡是支持565格式还是555格式,然后使用适当的方式。这是很讨厌的,但你坚持用16-bit色彩,这是没有办法避免的。由于存在两种格式,你就需要两种宏:

#define RGB_16BIT565(r, g, b) ((r << 11) | (g << 5) | (b))
#define RGB_16BIT555(r, g, b) ((r << 10) | (g << 5) | (b))

对于565格式,红色和蓝色的取值范围是0——31,绿色是0——63;对于555格式,取值范围都是0——31,所以当要创建一个白色象素时,就会有所不同:

USHORT white_pixel_565 = RGB_16BIT565(31, 63, 31);
USHORT white_pixel_555 = RGB_15BIT555(31, 31, 31);

这个USHORT是无符号短实数类型,对应的变量只有16位。存在两种格式把事情搞得有些复杂,但在实际的游戏编程过程中,你将会感觉到这并没有你想象的那么讨厌。顺便说一下,有些时候555格式被称为15-bit色彩深度,所以在以后如果我这样谈到了它,你一定要心领神会哦!^_^
现在或许是告诉你在16-bit色彩深度模式下,怎样检测显示卡到底支持哪种格式的时机了,是555还是565呢?最简单的办法就是调用IDirectDrawSurface7接口下的GetPixelFormat()函数,它的原形如下:

HRESULT GetPixelFormat(LPDDPIXELFORMAT lpDDPixelFormat);

参数是指向DDPIXELFORMAT结构的指针。你只要声明它,初始化它,然后传递它的地址就一切OK了。这个结构的本身是巨大的,所以我就不列举它了,但我得告诉你它的三个成员,都是DWORD类型的,它们是dwRBitMaskdwGBitMask、和dwBBitMask。你可以从dwRBitMaskdwGBitMaskdwBBitMask中获得掩码值(新东东,先不用太明白)。你也可以用它们检测显示卡支持的格式。如果显示卡支持565dwGBitMask将为0x07E0。如果是555格式,dwGbitMask0x03E0
现在,我们已经学习了所有我们可能用到的象素格式,可以进入在DirectX下显示图象的实际阶段了。你已经等待了很久了,不是吗?在把象素放到表面上前,我们需要锁定表面,至少是锁定表面的一部分。锁定表面返回一个指向表面在内存里位置的指针,然后,我们就可以为所欲为了。

锁定表面

待续。。。。。。

阅读(2590) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册