代码改变世界

如何开发绚丽、高效率的界面(Windows嵌入式系统)(四)

2009-12-24 21:14 王克伟 阅读(...) 评论(...) 编辑 收藏

上一篇文章:如何开发绚丽、高效率的界面(Windows嵌入式系统)(三)

3.DirectDraw介绍

这一部分是对DirectDraw更深入一点的介绍,但是仍然是你做DirectDraw开发所必需知道的知识。

Device-Independent Bitmaps(设备无关位图)

Windows Embedded CE and DirectX use the device-independent bitmap (DIB) as their native graphics file format.

A DIB is a file that contains information describing the following:
An image's dimensions,
The number of colors the image uses,
Values describing the colors used,
Data that describes each pixel.

A DIB also contains lesser-used parameters, like:
Information about file compression,
Significant colors (if all are not used),
Physical dimensions of the image (in case it will end up in print).

DIB files usually have the .bmp file extension, although they can use a .dib extension.

 

DirectDraw Object Types(各种类型的对象)

这里对各个常用对象继续简单介绍,更详细的介绍在后面。

DirectDraw Object

The DirectDraw object is the heart of all DirectDraw applications. It is the first object you create, and you use it to make all other related objects.

You create a DirectDraw object by calling the DirectDrawCreate function. DirectDraw objects expose their functionality through the IDirectDraw interface.

(Only one instance of DirectDraw can be created at a time for each process. If DirectDrawCreate is called when the process already has a DirectDraw object, another reference to the same DirectDraw object will be returned.)

The DirectDraw object represents the display device and makes use of hardware acceleration if the display device for which it was created supports hardware acceleration.

DirectX使用了COM技术,需要遵循COM规范,比如:

When you use the IDirectDraw interface to create a child object like a surface, the child uses the IUnknown::AddRef method of the parent DirectDraw object to increment the parent's reference count.
When your application no longer needs an object, call the Release method to decrement its reference count.

When the count reaches zero, the object is removed from memory.

When a child object's reference count reaches zero, it calls the parent's IUnknown::Release method to indicate that there is one less object who will be needing the parent's services.

You can only release a DirectDraw object from the thread that created the application window.

DirectDrawClipper Object

The DirectDrawClipper object (casually referred to as a clipper) helps you prevent blitting to certain portions of a surface or beyond the bounds of a surface.

You can create a clipper by calling the IDirectDraw::CreateClipper method.

DirectDrawClipper objects expose their functionality through the IDirectDrawClipper interface.

DirectDrawColorControl Object

The DirectDrawColorControl object allows you to get and set color controls. This object is accessed through the IDirectDrawColorControl interface.

DirectDrawSurface Object

The DirectDrawSurface object (casually referred to as a surface) represents an area in memory that holds data to be displayed on the monitor as images or moved to other surfaces. You usually create a surface by calling the IDirectDraw::CreateSurface method of the DirectDraw object with which it will be associated. DirectDrawSurface objects expose their functionality through the IDirectDrawSurface interface.

 

Surfaces(表面)

在上面我们已经简单的介绍了Primary Surface和Off-screen Surfaces,这里详细介绍Surface相关的概念:

A surface, or DirectDrawSurface object, represents a linear area of display memory. A surface usually resides in the display memory of the display card, although surfaces can exist in system memory.

Primary Surface
The primary surface is the surface currently visible on the monitor and is identified by the DDSCAPS_PRIMARYSURFACE flag.
You can only have one primary surface for each DirectDraw object.

Off-screen Surfaces
An off-screen surface is often used to cache bitmaps that will later be blitted to the primary surface or a back buffer.
Complex Surfaces
A complex surface is a set of surfaces created with a single call to the IDirectDraw::CreateSurface method.

Client Memory Surfaces
Client memory surfaces are simply DirectDrawSurface objects that use system memory that your application has previously allocated to hold image data. Creating such a surface is not common, but it is not difficult to do and it can be useful for applications that need to use DirectDraw surface capabilities on existing memory buffers.

Back buffer Surface、Overlay Surfaces、Flipping Chains、Color Keying、Clipper

Pitch vs. Width
Pitch is the distance, in bytes, between two memory addresses that represent the beginning of one bitmap line and the beginning of the next bitmap line. Because pitch is measured in bytes rather than pixels, a 640×480×8 surface will have a very different pitch value than a surface with the same dimensions but a different pixel format.

Flipping Surfaces
When you use the IDirectDrawSurface::Flip method to request a surface flip operation, the pointers to surface memory for the primary surface and back buffers are swapped. Flipping is performed by switching pointers that the display device uses for referencing memory, not by copying surface memory.

clip_image002

Blitting

Bit block transfer which is the process of transferring blocks of data from one place in memory to another.

Page Flipping and Back Buffering

The first surface is referred to as the primary surface, and the surfaces behind it are called back buffers.

Alpha Blitting
To perform blits that use and preserve alpha channel information, use the IDirectDrawSurface::AlphaBlt method. This method is currently unique to Windows Embedded CE, and fully supports alpha channel information during blit operations.

Transparent Blitting
Transparent blitting enables you to create the illusion of nonrectangular blits when animating sprites. A sprite image is usually nonrectangular, but blits are always rectangular, so every pixel within the sprite's bounding rectangle becomes part of the data transfer.

With transparent blitting, each pixel that is not part of the sprite image is treated as transparent when the blitter is moving the image to its destination, so that it does not overwrite the color in that pixel on the background image.
(指定透明色方式)

Enumerating Surfaces
By calling the IDirectDraw::EnumSurfaces method you can request that DirectDraw enumerate surfaces in various ways.

The EnumSurfaces method enables you to look for surfaces that fit, or do not fit, a provided surface description.

Accessing Surface Memory Directly
You can directly access the frame buffer or off-screen surface memory by using the IDirectDrawSurface::Lock method.

When you finish accessing the surface memory, call the IDirectDrawSurface::Unlock method to unlock it.

Using Color Controls
You set and retrieve surface color controls through the IDirectDrawColorControl interface, which can be retrieved by querying the DirectDrawSurface object using the IID_IDirectDrawColorControl reference identifier.

Color control information is represented by a DDCOLORCONTROL structure, which is used with both methods of the interface, IDirectDrawColorControl::SetColorControls and IDirectDrawColorControl::GetColorControls.
The DDCOLORCONTROL structure members can contain values that describe the brightness, contrast, hue, saturation, sharpness, gamma, and whether color is used.

Overlay Surfaces

a.Overlay surfaces, casually referred to as overlays, are surfaces with special hardware-supported capabilities.

Overlay surfaces are frequently used to display live video, recorded video, or still bitmaps over the primary surface without blitting to the primary surface or changing the primary surface's contents in any way.

DirectDraw does not emulate overlay surfaces.

b.Overlay Surfaces的原理

In fact, the mechanics of overlays work much like the clear plastic analogy.

While the display device paints scan lines to the monitor, it checks the location of each pixel in the primary surface to see if an overlay should be visible there instead. If so, the display device substitutes data from the overlay surface for the corresponding pixel.

By using this method, the display adapter produces a composite of the primary surface and the overlay on the monitor, providing transparency and stretching effects, without modifying the contents of either surface.
The composite surfaces are injected into the video stream and sent directly to the monitor. Because this on-the-fly processing and pixel substitution is handled at the hardware level, no noticeable performance loss occurs when displaying overlays.

c.Overlay Surfaces的创建

You create overlay surfaces by calling the IDirectDraw::CreateSurface method, specifying the DDSCAPS_OVERLAY flag in the associated DDSCAPS structure.

Overlay surfaces can only be created in video memory, so you must also include the DDSCAPS_VIDEOMEMORY flag.

As with other types of surfaces, by including the appropriate flags you can create either a single overlay or a flipping chain made up of multiple overlay surfaces.

To display an overlay surface, you call the overlay surface's IDirectDrawSurface::UpdateOverlay method, specifying the DDOVER_SHOW flag in the dwFlags parameter.

d.Boundary and Size Alignment

There are two types of restrictions, boundary restrictions and size restrictions. Both types of restrictions are expressed in terms of pixels (not bytes) and can apply to the source and destination rectangles. Also, these restrictions can vary depending on the pixel formats of the overlay and primary surface.

e.Minimum and Maximum Stretch Factors

Due to hardware limitations, some devices restrict how wide a destination rectangle can be compared with the corresponding source rectangle.

DirectDraw communicates these restrictions as stretch factors. A stretch factor is the ratio between the widths of the source and destination rectangles.

If the driver provides information about stretch factors, it sets the DDCAPS_OVERLAYSTRETCH flag in the DDCAPS structure after you call the IDirectDraw::GetCaps method.

f.Overlay Color Keys

Overlay color keys, like their blit-related counterparts, have a source version and a destination version that you set by calling the IDirectDrawSurface::SetColorKey method.

You use the DDCKEY_SRCOVERLAY or DDCKEY_DESTOVERLAY flags to set a source or destination overlay color key.

g.Overlay Z-Orders

DirectDraw supports overlay z-ordering to manage the order in which overlays clip each other. Z-order values represent conceptual distances from the primary surface toward the viewer.

They range from 0, which is just on top of the primary surface, to 4 billion, which is as close to the viewer as possible, and no two overlays can share the same z-order.

You set z-order values by calling the IDirectDrawSurface::UpdateOverlayZOrder method.

Converting Color and Format:

Non-RGB surface formats are described by four-character codes (FOURCC).

If an application calls the IDirectDrawSurface::GetPixelFormat method to request the pixel format, and the surface is a non-RGB surface, the DDPF_FOURCC flag will be set and the dwFourCC member of the DDPIXELFORMAT structure will be valid.

If the FOURCC code represents a YUV format, the DDPF_YUV flag will also be set and the dwYUVBitCount, dwYBitMask, dwUBitMask, dwVBitMask, and dwYUVAlphaBitMask members will be valid masks that can be used to extract information from the pixels.

If an RGB format is present, the DDPF_RGB flag will be set and the dwRGBBitCount, dwRBitMask, dwGBitMask, dwBBitMask, and dwRGBAlphaBitMask members will be valid masks that can be used to extract information from the pixels.

Surfaces and Device Contexts:

If you want to modify the contents of a DirectDraw surface object by using GDI functions, you must retrieve a GDI-compatible device context handle. You can use IDirectDrawSurface::GetDC.

You can retrieve a pointer to a surface's IDirectDrawSurface interface from the device context for the surface by calling the IDirectDraw::GetSurfaceFromDC method.

This functionality might be very useful for applications or ActiveX controls, that are commonly given a device context to draw into at run-time, but could benefit by exploiting the functionality exposed by the IDirectDrawSurface interface.

 

Clippers(剪切板)

Clippers, or DirectDrawClipper objects, allow you to blit to selected parts of a surface represented by a bounding rectangle or a list of several bounding rectangles.

我们经常利用Clippers来
One common use for a clipper is to define the boundaries of the screen or window. For example, imagine that you want to display a sprite as it enters the screen from an edge. You do not want to make the sprite pop suddenly onto the screen; you want it to appear as though it is smoothly moving into view.

Clip List
To manage the clip list yourself, create a list of rectangles in the form of a RGNDATA structure and pass this to the IDirectDrawClipper::SetClipList method.

To have DirectDraw manage the clip list for a primary surface, you attach the clipper to a window (even a full-screen window) by calling the IDirectDrawClipper::SetHWnd method, specifying the target window's handle.
If you set a clipper using a window handle, you cannot set additional rectangles.
Clipping for overlay surfaces is supported only if the overlay hardware can support clipping and if destination color keying is not active.

这样来使用
DirectDrawClipper objects can be shared between multiple surfaces. For example, the same DirectDrawClipper object can be set on both the front buffer and the back buffer of a flipping chain. When an application attaches a DirectDrawClipper object to a surface by using the IDirectDrawSurface::SetClipper method, the surface increments the reference count of that object.

Driver-independent DirectDrawClipper objects are created by using the new IDirectDraw::CreateClipper DirectDraw function.

An application can call this function before any DirectDraw objects are created.

 

Cooperative Levels(坐标系)

使用DirectDraw cooperative levels来决定你的应用程序作为一个a full-screen program with exclusive access to the display还是a windowed application.

前者可以使用perform page flipping,而后者不可以。

使用后者时调用IDirectDraw::SetCooperativeLevel时不指定窗口句柄即可让你所有的窗口使用Cooperative Level。

At the full-screen and exclusive cooperative level, you can use the hardware to its fullest. In this mode, you can implement page flipping.

The exclusive (full-screen) mode prevents other applications from allocating some surface types and from drawing to the primary display. The exclusive mode also prevents other windows on the system from coming to the foreground, so DirectDraw applications must watch for system events (such as incoming phone calls) so they can relinquish control back to the system when necessary.

SetCooperativeLevel maintains a binding between a process and a window handle. If SetCooperativeLevel is called once in a process, a binding is established between the process and the window.

使用Cooperative Levels有如下好处

Prevent DirectDraw from releasing exclusive control of the display.
Enable DirectDraw to minimize or maximize the application in response to activation events.

IDirectDraw::TestCooperativeLevel
This method reports the current cooperative-level status of the DirectDraw device for a windowed or full-screen application.

Return Value
If the method succeeds, the return value is DD_OK, indicating that the calling application can continue executing.

If the method fails, the return value may be one of the following error values:
DDERR_INVALIDOBJECT
DDERR_EXCLUSIVEMODEALREADYSET(An attempt was made to set the cooperative level when it was already set to exclusive.)
DDERR_WRONGMODE(This surface cannot be restored because it was created in a different mode. )

 

Display Modes(显示模式)

A display mode is a hardware setting that describes the dimensions and bit-depth of graphics that the display hardware sends to the monitor from the primary surface. Display modes are described by their defining characteristics: width, height, and bit-depth. For instance, most display adapters can display graphics 640 pixels wide and 480 pixels tall, where each pixel is 8 bits of data. In shorthand, this display mode is called 640×480×8.

There are two types of display modes: palettized and non-palettized. For palettized display modes, each pixel is a value representing an index into an associated palette.

前者的一个例子:In an 8-bit palettized display mode, each pixel is a value from 0 to 255. In such a display mode, the palette can contain 256 entries.

Non-palettized display modes, as their name states, do not use palettes. The bit depth of a non-palettized display mode indicates the total number of bits that are used to describe a pixel.

Windows Mobile/Windows Embedded CE上的DD与Desktop上在Display Modes不同的地方
1.DirectDraw cannot change the display mode. All display mode related methods are for information retrieval purposes only.

2.DirectDraw now supports screen rotation, if rotation is supported by the display driver. When the screen rotates, any surfaces that change will be lost, and can be restored using IDirectDrawSurface::Restore. If a surface is locked, the surface pointer (lpSurface) will always point to the (0,0) coordinate of the surface, but the lPitch (vertical pitch) and lXPitch (horizontal pitch) can vary depending on the orientation of the surface.

3.在CE上When using multiple screens, DirectDraw will only work on the primary display device. All other screens are left under the control of the graphics device interface (GDI).

还有以下相关API:

IDirectDraw::EnumDisplayModes
IDirectDraw::SetDisplayMode

 

DirectDraw Hardware Abstraction Layer(硬件抽象层)

The device manufacturer implements the HAL in a combination of 16-bit and 32-bit code under Windows. The HAL can be part of the display driver or a separate DLL that communicates with the display driver through a private interface that driver's creator defines.

The DirectDraw HAL is implemented by the chip manufacturer, board producer, or OEM. The HAL implements only device-dependent code and performs no emulation.

 

Software Emulation(软件模拟实现)

When the hardware does not support some functionality through the hardware abstraction layer (HAL), DirectDraw attempts to emulate it. This emulated functionality is provided through the hardware emulation layer (HEL).

You can query for the capabilities that the hardware supports by using the IDirectDraw::GetCaps method.

The HEL is capable of supporting 8, 16, 24, and 32 bpp surfaces with a variety of different bit masks.

In some cases, certain combinations of hardware-supported capabilities and emulation can result in slower performance than emulation alone. For example, if the display device driver supports DirectDraw but not stretch blitting, noticeable performance losses will occur when stretch blitting from video memory surfaces. This happens because video memory is often slower than system memory, forcing the CPU to wait when accessing video memory surfaces.

If your application uses a capability that is not supported by the hardware, it is sometimes best to create surfaces in system memory, thereby avoiding performance losses created when the CPU accesses video memory.

 

GAPI、GDI、GDI+、DirectDraw等的关系

在文章《如何开发绚丽、高效率的界面(Windows嵌入式系统)(二)》我介绍到了GDI和GDI+,那么GDI和DirectDraw的又有什么关系:

"The drawback of GDI is that it was not designed for high-performance multimedia software. It was made to be used by business applications like word processors and spreadsheet applications.

GDI provides access to a video buffer in system memory, not video memory, and does not take advantage of special capabilities that some video cards provide. In short, GDI is great for most business applications, but its performance is too slow for multimedia or game software.”

下图是DirectDraw的架构,从中我们看到DirectDraw走最右边的一条线时即使用了硬件加速,硬件不支持需要用Software Emulation(软件模拟实现)时会走Hardware Emulation Layer这条线,其实调用的是GDI的API(因为需要CPU去处理,所以叫做软件模拟实现)。

clip_image002[7] 

 

GAPI

GAPI是非常古老的用于在Pocket PC上开发高效率的游戏,那时因为Pocket PC上不支持DirectDraw。但是在Windows Mobile 5.0以后微软逐渐取消了这个API。看官方的博客(http://blogs.msdn.com/windowsmobile/archive/2007/08/13/have-you-migrated-to-directdraw-yet.aspx)说明:

"When we released Windows Mobile 5.0, we marked GAPI as deprecated in the SDK docs.  Deprecated means that we won’t be updating the API and that it may be removed in future releases of Windows Mobile.  GAPI was superseded by DDraw which is a more complete and robust way of accessing the screen for graphics.

Caveat : We know DirectDraw doesn’t provide input, for the purposes of this discussion, we’re talking only about accessing the screen for drawing.

If you’re using GAPI and haven’t yet migrated to DirectDraw (or Direct3D Mobile), please take a moment to add a comment letting us know why you haven’t yet made the switch.  Is DirectDraw/Direct3D Mobile missing something you need?  Do you need documentation to help migrate from GAPI to DDraw?  Does GAPI work “well enough” so you don’t need to migrate?  If GAPI is “good enough” what might make you migrate to DirectDraw?

We’d also love to hear from you if you’ve already made the switch from GAPI to DirectDraw!  What was the experience like?  What went well?  What could have gone better?”

M8(Windows Embedded CE系统)上GAPI 演示的《古墓丽影》的一个视频片段:http://mv.2u.com.cn/detail_352339.html

另外如果你对M8上的GAPI感兴趣请点这里:http://down.m8fans.com/soft-91.html

在很久很久以前马宁老师写了一篇Game API的入门介绍,如果你还感兴趣的话点击这里

以下是更多可以参考的文章:
http://blog.csdn.net/dotnet_editor/archive/2005/08/19/459027.aspx#719659
http://www.cnblogs.com/bluepointcq/archive/2006/03/31/363704.html
http://blog.csdn.net/JamesXing/archive/2008/01/10/2033737.aspx
http://blog.csdn.net/benny5609/archive/2008/05/22/2468800.aspx

 

4.DirectDraw驱动开发

由于篇幅原因暂时略过。

 

5.DirectDraw应用开发

以下展示的是一段简单的从创建DirectDraw对象,设置坐标系,创建Surface,然后修改(57,97)位置的像素为红色的代码:

	#include <ddraw.h>

HRESULT hr;
IDirectDraw * pDD = NULL;
IDirectDrawSurface * pDDSPrimary = NULL;
DDSURFACEDESC ddsd;

hr = DirectDrawCreate(NULL, &pDD, NULL);
hr = pDD->SetCooperativeLevel(hwnd, DDSCL_FULLSCREEN);
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
hr = pDD->CreateSurface(&ddsd, &pDDSPrimary, NULL);

pDDSPrimary->Lock(NULL, &ddsd, DDLOCK_WAITNOTBUSY, NULL);
// Set pixel 57, 97 to Red (assuming RGB565 pixel fmt)
int x = 57;
int y = 97;
BYTE * pPixelOffset = (BYTE*)ddsd.lpSurface
+ x * ddsd.lXPitch
+ y * ddsd.lPitch;
*(WORD*)pPixelOffset = 0xf800;
hr = pDDSPrimary->Unlock();

更多的示例代码请见第6部分。

 

6.一个推荐的入门Sample

这个例子是比较完整的例子:

<6.0 SDK安装目录>\Samples\PocketPC\CPP\win32\directx\DDraw\Donuts2

我们简单分析一下:

a.程序的“发动机”部分包括消息泵和界面的不停刷新:

    while( 1 ) 
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{

if (msg.message == WM_QUIT)
{
retcode = (int)msg.wParam;
break;
}
if ( !TranslateAccelerator( hWndMain, hAccel, &msg ) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
else if ( bIsActive )
{
UpdateFrame();
}
}


b.游戏初始化包括:

InitializeSound,
创建DirectDraw对象:
    DirectDrawCreate( NULL, &lpDD, NULL );
设置坐标系:
    lpDD->lpVtbl->SetCooperativeLevel( lpDD, hWndMain, DDSCL_FULLSCREEN );
查看设备显示能力:
    lpDD->lpVtbl->GetCaps(lpDD, &ddCaps, &ddHelCaps);
查看显示模式信息:
    lpDD->lpVtbl->GetDisplayMode( lpDD, &DDSurfDesc );
创建DirectDraw Surface对象:
    lpDD->lpVtbl->CreateSurface( lpDD, &ddsd, &lpFrontBuffer, NULL );
    ...
修复Surface对象并将位图绘制到Surface中:
    lpFrontBuffer->lpVtbl->Restore(lpFrontBuffer);
    hbm = (HBITMAP)LoadImage( hInst, TEXT("DONUTS8"), IMAGE_BITMAP, 0, 0, 0 );
    DDCopyBitmap( lpDonut, hbm, 0, 0, 320, 384 );

    DDCopyBitmap主要做的工作是:

            pdds->GetSurfaceDesc(&ddsd); 

if ((hr = pdds->GetDC(&hdc)) == DD_OK)
{
if (!StretchBlt(hdc,
0, 0,
ddsd.dwWidth,
ddsd.dwHeight,
hdcImage,
x, y,
dx, dy,
SRCCOPY)) hr = E_FAIL;
pdds->ReleaseDC(hdc);
}


c.如何实时检查碰撞:

 CheckForHits
这些是游戏的算法,在此不再研究。

 

最后推荐一个在托管代码下开发的文章:

http://www.codeproject.com/KB/mobile/WindowsMoibleManagedDDraw.aspx

DirectDraw OS Design Development:

http://msdn.microsoft.com/en-us/library/aa919851.aspx