移动到平面(Blitting to Surfaces)


译者按: 我到现在也不知道Blit如何翻译,其意义是将一个平面的一部分或全部图象整块从这个平面复制到另一个平面。 以后我会用Blit代替这些麻烦的解释。

这部分阐述了如何将一个DirectDraw平面的像素复制到另一个,或是其一部分。
含有以下主题:

基本Blit

有两个宏可以复制图象到一个DirectDraw平面: IDirectDrawSurface4::Blt及IDirectDrawSurface4::BltFast。(还有第三个宏, IDirectDrawSurface4::BltBatch不在这个版本的DirectX中应用) 这些宏由目标平面调用并返回源平面参数。目标及源平面可以是同一个, 你不必担心重叠DirectDraw会在覆盖之前事先保护这些原来的像素。

对于两个应用的宏,Blt更灵活而BltFast更快,但这仅在非硬件的Blit。 你可以用IDirectDraw4::GetCaps宏中的lpDDDriverCaps 参数中包含的DDCAPS结构来决定硬件的Blit能力。如果dwCaps成员包含DDCAPS_BLT, 硬件就至少支持最小程度的Blit能力。


用BltFast来Blit

当使用IDirectDrawSurface4::BltFast, 你要在源平面提供一个有效的矩形以用来复制(或用NULL来指定整个平面), 以及目标平面的X及Y坐标。源矩形必须能够适合目标平面,从左上角的点 (换言之,源矩形不能大于目标平面),否则调用会失败并返回DDERR_INVALIDRECT值。 BltFast无法用于已附加剪切的平面。

没有伸拉,镜面(反转),或其他效果在BltFast中不被提供。

BltFast 例程:
以下例程将从一个屏下平面lpDDSOffOne复制像素到主平面lpDDSPrimary。 标志保证操作会发生于Blitter(Blit器)空闲时,并且源图象的透明色将不被复制。 (标志的具体含义参见,适时Blit透明Blit。)

lpDDSPrimary->BltFast(
        100, 200,    // Upper left xy of destination左上角的XY位置
        lpDDSOffOne, // Source surface源平面
        NULL,        // Source rectangle = entire surface源矩形=整个平面
        DDBLTFAST_WAIT | DDBLTFAST_SRCCOLORKEY );

用Blt来Blit

当使用IDirectDrawSurface4::Blt宏,你要在源平面提供一个有效的矩形以用来复制 (或用NULL来指定整个平面),及在目标平面中提供一个矩形来存放要复制的图象 (同样,NULL表示矩形包括整个平面)。如果目标平面附加剪切, 目标矩形的边界能超出平面并且会进行剪切。如果没有剪切器(Clipper), 目标矩形必须在平面以内或一致,否则调用宏失败返回DDERR_INVALIDRECT。 (详见剪裁,剪裁器。)

缩放比例

Blt宏会自动安排源图象填充于目标矩形。如果缩放比例并非是你所希望的, 要得到最佳性能请确认源及目标矩形是一个尺寸, 或干脆用IDirectDrawSurface4::BltFast。(详见用BltFast来Blit。)

缩放的硬件加速需要依靠设备的DDCAPS结构中的dwFXCaps成员的DDFXCAPS_BLT*标志。 如果,例如,一个设备有DDFXCAPS_BLTSTRECTCHXN能力但没有DDFXCAPS_BLTSTRECTCHX能力, 就可以支持源矩形的X坐标的所有成员的倍数增加,而不支持非整数(任意)的伸拉。

设备可能也支持算术伸拉,也就是通过插值运算而非简单增加或删除像素。比如, 如果一个坐标轴被拉长三分之一,针对原来的图象,像素会被重新着色为一个近似值, 不然就会在这个坐标轴上每隔三个像素点产生一个重叠色。

应用程序无法控制驱动程序的伸拉类型,除非设置传递给Blt的DDBLTFX结构的dwDDFX 成员中DDBLTFX_ARITHSTRETCHY标志。这个标志请求算术伸拉可以在Y轴上进行。 X轴的算术伸拉及算术收缩在现在的DirectDraw API中不被支持,但驱动程序可能默认提供。

其他效果

如果你不需要在使用Blt时使用其他特殊效果,你可以在lpDDBltFx参数中传递NULL。 或者你可以选择DDBLTFX结构中定义的多种特殊效果。其中包括,HEL(硬件模拟层) 提供的色补充及镜面(反转),所以它们总能使用。多数其他效果需要硬件的支持。

对于完整查看HEL的效果能力,运行程序员手册的DDraw Caps工具从HEL菜单选择 HEL FX Caps。对于不同标志的解释,参见DDCAPS。 你可以在你的程序中调用IDirectDraw4::GetCaps宏来测试HEL能力。

当你指定一个效果需要传递一个IDirectDrawSurface4::Blt宏的DDBLTFX 结构中的一个成员的参数,你必须也在dwFlags参数中包含适当的标志来告诉结构中的那个成员是有效的。

一些效果只需要在DDBLTFX的dwFlags成员设置标志。其中一个是DDBLTFX_NOTEARING。 你可以在直接Blit动画图象到前景缓冲使用这个标志,使其绘制与屏幕刷新率一样并可以使抖动减少到最小。 镜面(反转)及旋转也使用这些标志。

Blit效果包含标准的光栅操作(ROPs)使用GDI函数如BitBlt。 ROPs只支持的HEL有SRCCOPY(默认),BLACKNESS及WHITENESS。 硬件支持的其他ROPs可以在驱动程序的DDCAPS结构中查询, 如果你要在Blt宏中使用标准的ROPs,你要在DDBLTFX结构中的dwROP成员中标出。

DDBLTFX结构的dwDDROP成员是用来指定DirectDraw的ROPs特性。不管怎样,没有什么ROPs在当前被定义。

Alpha及Z值

不透明及深度值在现在的DirectDraw Blit中不被支持。如果Alpha值存在于像素格式中,他们会简单覆盖目标矩形的Alpha值。 Alpha缓冲及Z-缓冲的值被越过。DDBLTFX结构成员与Alpha通道及Z-缓冲 (成员名是一些开始为dwAlpha及dwZ),及与Blt的一致的标志都未被使用。 同样于DDBLTFX结构的dwDDFX成员中的DDBLTFX_ZBUFFERBASEDEST及DDBLTFX_ZBUFFERRANGE标志。

虽然Z-缓冲现在仅用于Direct3D应用程序中,你可以用IDirectDrawSurface4::Blt 来为一个Z-缓冲平面设置一个深度值,通过设置DDBLT_DEPTHFILL标志。 详见,清除深度缓冲。要了解Alpha通道及Z-缓冲在Direct3D中的应用, 参见以下主题:


适时Blit

使用IDirectDrawSurface4::Blt或IDirectDrawSurface4::BltFast 的任何一个来复制像素到一个平面,宏可能失败并收到DDERR_WASSTILLDRAWING, 因为硬件Blit器还未准备好接受命令。

如果你的程序在等待Blit器恢复到预备状态时没有特别急的需要, 你可以在Blt的dwFlag参数中指定DDBLT_WAIT,或为BltFast指定DDBLYFAST_WAIT标志。 这个标志会使宏等待直到Blit器着手进行Blit(或到除DDERR_WASSTILLDRAWING以外的错误发生)。 Blt支持另一个标志,DDBLT_ASYNC,可以发挥一些硬件的FIFO(先进先出)队列性能。


透明Blit

这部分讨论用透明Blit来有选择性得复制矩形图形的部分的理论与实践, 使用源及目标色键(译者按:或称关键色)。
概念以以下主题作为开头:

什么是透明Blit?

透明Blit允许你在动态精灵(Sprite)创建非矩形Blit。 一个精灵图形通常是非矩形的,Blit通常是矩形的, 所以每个精灵的边界都是以矩形来进行数据传输的。 当用透明Blit,当Blit器在移动图相到目标时不是精灵部分的像素点将被视作透明, 所以就不覆盖背景图形的该像素。

美工创造精灵并选择一个任意色或一个色段来用作透明色键。这通常是一个未用色, 仅仅用于透明色,并用来填充所有不需复制的图形部分。在运行时你设置平面包括精灵的色键。 (如果你想的话,你可以自动设置图象的左上角一点的像素色为色键)随后的Blit就会使用色键, 越过匹配的像素。这类的色键就是所提到的源色键。

你也可以在目标平面上使用色键,如果硬件支持目标色键。 这个目标色键则可以被精灵覆盖。例如,有一个前景图象,精灵从其后面移动, 比如一个有窗的墙。美工选择一个无用的色,不被图形所使用的色,来描绘窗外的天空。 当你在目标平面设置这个色键并Blit精灵到该平面,精灵像素只会覆盖使用了色键色的部分, 在这个例子中,精灵只会出现在窗口中,不会出现在墙或窗框上。结果,精灵看上去就象在屋外。

源色键与目标色键可以被合并。在例子中,精灵可以使用一个源色键, 使其整个边界不会完全显示在天空背景上。

色键格式

色键是在DDCOLORKEY结构中来描述。如果色键是一个单一的色, 这个结构的所有成员都应被设置成同一个值。否则,色键就是一个色彩范围。

色键指定为平面的像素格式。如果是基于调色板格式,色键就是色彩索引或一段索引范围。 如果平面格式是指定为FOURCC码并描述为YUV格式,YUV色键应在DDCOLORKEY结构中的 dwColorSpaceLowValue及dwColorSpaceHighValue的成员都设置成三个低顺序字节。 最低的字节包含V数据,第二低的字节包含U数据,最高的字节包含Y数据。

一些有效的色键例程:

    8-Bit调色板模式
//调色板通道26是色键
dwColorSpaceLowValue=26;
dwColorSpaceHighValue=26;

    24-Bit真彩色模式
//色 255,128,128是色键
dwColorSpaceLowValue=RGBQUAD(255,128,128);
dwColorSpaceHighValue=RGBQUAD(255,128,128);

    FourCC YUV模式
//任何YUV色,Y是100与110之间,U和V是50与55之间的是透明色
dwColorSpaceLowValue=YUVQUAD(100,50,50);
dwColorSpaceHighValue=YUVQUAD(110,55,55);

支持色键段与单一色键不同需要硬件支持。查看硬件的DDCAPS结构中的dwCKeyCaps成员。HEL不支持色键段。

一些硬件只支持YUV像素数据的色键段,通常用于视频。视频中的透明背景("蓝屏") 可能不是单一的纯色,所以用一个色键段更理想。

设置色键

你可以在创建平面的前后来设置其源及目标色键。

要在创建平面时设置色键,你需要在传递给IDirectDraw4:CreateSurface 的DDSURFACEDESC2结构中的ddckCKSrcBlt及ddckCKDestBlt成员赋于适当的值。 要使色键Blit生效,你也需要在dwFlags成员中加上DDSD_CKSRCBLT及DDSD_CKDESTBLT的一个或全部。

要为已创建好的平面设置色键,你要使用IDirectDrawSurface4::SetColorKey宏。 你在lpDDColorKey指定一个键并设置dwFlags成员中的DDCKEY_SRCBLT及DDCKEY_DESTBLT 的一个指定你是设置源色键或目标色键。如果DDCOLORKEY结构包含一个色键段, 你必须也设置DDCKEY_COLORSPACE标志。如果这个标志未设置,结构中只有dwColorSpaceLowValue成员会被使用。

依靠色键来Blit

如果你要在调用IDirectDrawSurface4::BltFast宏时使用平面色键, 你必须设置dwTrans参数中的DDBLTFAST_STCCOLORKEY或DDBLTFAST_DESTCOLORKEY标志的一个或全部。 为了要在调用IDirectDrawSurface4::Blt时使用色键,你要传递在dwFlags参数中的 DDBLT_KEYSRC及DDBLT_KEYDEST标志的一个或全部。作为选择,你能为DDBLTFX结构中的 ddckDestColorkey及ddckSrcColorkey成员设置适当的色彩值,并通过lpDDBltFx成员传递给宏。 在这中情况下,你必须也设置dwFlags参数中的DBLT_KEYSRCOVERRIDE及DDBLT_KEYDESTOVERRIDE 标志的一个或全部,以便选择的色键能被DDBLTFX带走而不是平面的其他属性。

色填充


色填充

为了要在平面所有的或一部分填充一个单一的色,你可以使用带有DDBLT_COLORFILL 标志的IDirecyDrawSurface4::Blt宏。这个技术可以使你快速擦除一个区域或画实心背景。

以下例程在获得像素格式中蓝色值后,填充一个平面的全部为蓝色:

/* It is assumed that lpDDS is a valid pointer to 
   an IDirectDrawSurface4 interface. */
/* 假定lpDDS对于一个IDirectDrawSurface4界面是一个有效的指针。*/
 
HRESULT ddrval;
DDPIXELFORMAT ddpf;
 
ddpf.dwSize = sizeof(ddpf);
if (SUCCEEDED(lpDSS->GetPixelFormat(&ddpf))
{
    DDBLTFX ddbltfx; 
 
    ddbltfx.dwSize = sizeof(ddbltfx); 
    ddbltfx.dwFillColor = ddpf.dwBBitMask; // Pure blue 纯蓝
 
    ddrval = lpDDS->Blt( 
        NULL,        // Destination is entire surface,目标是整个平面
        NULL,        // No source surface,没有源平面
        NULL,        // No source rectangle  没有源矩形
        DDBLT_COLORFILL, &ddbltfx); 
 
    switch(ddrval) 
    { 
        case DDERR_WASSTILLDRAWING: 
            . 
            . 
            . 
        case DDERR_SURFACELOST: 
            . 
            . 
            . 
        case DD_OK: 
            . 
            . 
            . 
        default: 
    } 
}

Blit到多窗口

你可以用一个DirectDraw对象及一个DirectDrawClipper对象来Blit 到由运行在普通协作层的程序创建的多窗口。详细情况,参见在多窗口使用Clip。

不建议创建多个DirectDraw对象,然后Blit到每个其他的主平面。


上篇|返回|下篇

Lucker 1999.5.16
E-Mail: fred_cai@kali.com.cn


云风工作室 制作