Java中使用DirectDraw
注释:DirectDraw®是微软DirectX® SDK的一个组成部分。Java版的DirectX包含在Java 2.0 SDK中。Java中通过同DirectX一起安装的com.ms.directX包中的一套类访问DirectDraw。
介绍
本文将探讨用于Java的DirectDraw SDK的一些优点、结构和使用。过去,使用动画的程序需要用C++编写(或者汇编语言),原因是动画需要很快的处理速度。动画是通过快速连续地显示一系列画面(或者叫做帧)实现的。为了使用户观看动画时没有闪烁感,至少需要达到每秒12帧的速率。更高的帧速率会产生更平滑的动画:例如,动画片的帧速率通常为每秒24帧。要达到最低的每秒12帧要求,在640´480,256色模式下每秒需要处理3.6M字节的图象数据。处理如此数量的视频数据是非常困难的。DirectDraw现在为Java开发人员提供了这种视频数据处理能力。
开始使用DirectDraw API需要一些基本步骤。现将其列举如下,并且将会在后面的部分中解释:
创建DirectDraw对象。
设置协作级别。
创建需要的表面。
装入需要的位图。
显示表面。
DirectDraw对象
要在Java中使用DirectDraw,必须先创建DirectDraw对象:
dd = new DirectDraw();
一旦创建了DirectDraw对象,就需要设置协作级别。
协作级别
必须要设置DirectDraw在最高层窗口上拥有的控制级别。在最简单的级别上,DirectDraw的功能同其他程序相同,限制在Windows® 95或者Windows NT®窗口内。这是DirectDraw的正常级别操作。正常协作级别可以通过下面的语句设置:
dd.setCooperativeLevel(hwnd, DDSCL_NORMAL);
注意 为了使程序能够利用更高级的DirectDraw特性,如改变显示模式或者修改DirectDraw的表面的行为,需要使用独占模式。然而,使用高级特性超出了本文的范围。要得到关于使用高级特性的更多信息,请参考DirectX文档。
创建表面
DirectDraw使用术语表面(surface)代表显示内容。表面既可以放在显示内存中,也可以放在系统内存中。然而,如果显示硬件没有足够的内存存放表面,DirectDraw会在系统内存中模拟表面。DirectDraw表面的最大优点是它总是以线性内存区域的形式提供给开发者。即使程序设置的显示模式不是线性的,表面为开发者提供的仍然是线性内存区域。考虑下面的例子,ModeX中显示内存是以一系列位面(plane)配置的(图1)。为了画一个点,必须防卫适当的内存位面以及正确的地址。DirectDrawSurface对象处理了所有这些细节。
图 1. Mode X中的显示内存组织。
一个DirectDraw表面可以包含多个内存缓冲区,允许构建可以切换的表面。切换表面使程序可以利用双缓冲技术的优点。在双缓冲方法中,程序在后台缓冲区中绘制一些内容,画完后,快速地将后台缓冲区切换或者复制到前台缓冲区,使该画面显示出来。双缓冲方法速度非常快,尤其是当前台和后台缓冲区都在显示内存中时。此时,切换操作甚至不消耗任何CPU时间。
基于DirectDraw程序的用户看到的总是原始表面(Primary surface)。为了改变用户看到的内容,可以简单地改变原始表面的内容。通常,需要创建一个或多个后台表面,这些表面中包含不同时间显示的画面。图2说明了一个复杂的双缓冲原始表面。该原始表面由一个后台缓冲区、一个前台缓冲区和四个后台表面组成。每个后台表面包含一个不同旋转阶段的位图。要使圆柱体动起来,程序将每个位图从后台表面中复制到原始表面的后台缓冲区中的一个位置。每当每个圆柱体的复制完成时,程序将后台缓冲区切换到前台缓冲区。
图2. 双缓冲表面之间的关系
DirectDraw在Java中的实现包括两个为表面工作而特别设计的类。DDSurfaceDesc类用来描述所创建表面的属性。DirectDrawSurface类包含为DirectDraw表面工作所做的定义、方法和变量。
要创建DirectDraw表面,不管是原始表面还是后台表面,必须先创建DDSurfaceDesc对象,设置表面属性,并且把表面描述结构传递给DirectDraw对象的CreateSurface()方法。下面的语句创建了一个由一个缓冲区组成的原始表面:
// Create a primary surface containing a single buffer.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS ;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE;
pdds = dd.createSurface( ddsd );
下面的语句创建了一个双缓冲原始表面:
//Create a primary surface containing a back //buffer for double buffering.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps = DDSCAPS_PRIMARYSURFACE | DDSCPAS_FLIP | DDSCAPS_COMPLEX;
ddsd. BackBufferCount = 1;
pdds = dd.createSurface( ddsd );
下面的语句说明了如何创建一个320象素宽200行高的后台表面:
Ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.width = 320;
ddsd.height = 200;
pdds = dd.createSurface( ddsd );
将位图装入到DirectDraw表面中
位图是程序可以用来显示图形内容的图片。图2中的四个圆柱体每一个都是一张位图。Java中的DirectDraw为操作位图文件提供了DirectDrawBitmap类。DirectDrawBitmap对象创建后,可以复制到DirectDraw表面。下面的语句将一个叫作Frntback.bmp的位图文件读入到DirectDraw表面:
//Read the bitmap file.
Bm = new DirectDrawBitmap();
bm.filename(“Frntback.bmp”);
bm.initWidth(dx);
bm.initHeight(dy);
if( bm.loaded() != 0 )
{
// Create a DirectDrawSurface for
// this bitmap.
ddsd = new DDSurfaceDesc();
ddsd.flags = DDSD_CAPS |
DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps = DDSCAPS_OFFSCREENPLAIN;
ddsd.width = bm.width();
ddsd.height = bm.height();
pdds = dd.createSurface( ddsd );
pdds.copyBitmap(bm, 0, 0, 0, 0);
}
//Off-screen surface pdds now contains the
//bitmap data.
显示表面
当所有表面都创建并初始化(例如将位图复制到表面中)以后,程序需要显示它们时就可以将它们复制到原始表面。这种表面复制通过DirectDrawSurface对象的blt方法完成。在缺省的操作模式下,如果切换程序忙(正在进行切换),DDS.blt()方法立即返回一个错误码。因此,应该以某种循环的形式使用DDS.blt()方法,或者指定DDS.blt()方法的DDBLT_WAIT标志。第二个选项改变了DDS.blt()的行为,使得此方法等待,直到切换可以进行或者出现另一个错误。
下面的代码说明了如何使用DirectDrawSurface的blt方法。首先,有两点需要阐明。如果显示卡的模式改变或者程序使用对显示卡的独占访问而释放了当前在显卡上分配的所有表面内存,DirectDrawSurface就会丢失。当表面丢失时,需要恢复。恢复可以通过两个步骤完成。首先调用DirectDrawSurface对象的Restore方法重新分配表面内存以及重新添加DirectDrawSurface对象。然后,重建相关表面的内容。
rc.Left = 0;
rc.Top = 0;
rc.Right = bm.width();
rc.Bottom = bm.height();
// Show the off-screen surface on the
//primary surface.
done = 0;
do
{
int retval;
retval = ddsPrimary.blt( rc, ddsOne, rc, 0);
if( retval == DD_OK )
{
//If the bitmap has been successfully
//copied, exit loop.
done = 1;
}
else if ( retval == DDERR_SURFACELOST )
{
while( ddsPrimary.Restore() != DD_OK &&
ddsOne.Restore() != DD_OK )
{
ReloadBitmap(ddsOne, szBitmap);
}
}
else if( retval != DDERR_WASSTILLDRAWING)
{
// Undetermined error; quit the loop.
done = 1;
}
} while( done == 0);
其他信息
本文提供了初始化和使用Java中DirectDraw SDK的背景和基本步骤。要获得Java的DirectX SDK以及相关文档,可以从http://www.microsoft.com/java/下载Microsoft SDK for Java 2.0(其中包含DirectX SDK)。其中还包括几个示例程序,对于使用DirectX for Java创建动画非常有用。这些例子列举如下:
示例
目录
示例内容
Ddex3
DDraw\ddex3\ddraw.html
DirectDraw的基本使用
Flipcube
d3d\flipcube\D3D.html
3D立即模式的基本使用
Viewer
d3drm\Viewer\direct3dRM.html
Direct3D保持模式的完整使用
Castle
d3drm\Castle\Castle.html
Direct3D保持模式的高性能
DirectInput
dInput\ddex3\dinput.html
操纵杆、鼠标、键盘和游戏键盘的使用