Introduction to DirectDraw and Surface Blitting

Introduction

Lots of people asked me to write an introductory article about DirectDraw programming and Spriting so that people can understand the basic concepts and start discovering the other things about DirectX from samples (MSDN and others available here). For all those that asked me the introductory article, here it is.

WinMain and Message Loop - The Starting point

Since we are working with a DirectX application, there is no need to use the MFC library in our program. Not that the use of MFC in a DirectX application is prohibited, but MFC has a lot of code aimed to desktop apps and not graphic intensive ones, so its better to stick on plain Windows API and STL. We will start our basic DirectDraw program by selecting the "Windows Application" option in the Visual C++ interface. At the first screen we will select the option "Simple Win32 Application" to allow Visual C++ to create a WinMain function for us. The code generated by the wizard will look like this:

#include "stdafx.h"

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    // TODO: Place code here.

    return 0;
}

Now that we have the main function of our program, we need to create a main window for the program so that we can allow Windows OS to send messages to our application. Even if you work with a full screen DirectX application you'll still need a main window in the background, so that your program can receive the messages that the system sends to it. We will put the window initialization routine in another function of our program, this function will be called InitWindow.

Collapse
HWND InitWindow(int iCmdShow)
{

   HWND      hWnd;
   WNDCLASS  wc;

   wc.style = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc = WndProc;
   wc.cbClsExtra = 0;
   wc.cbWndExtra = 0;
   wc.hInstance = g_hInst;
   wc.hIcon = LoadIcon(g_hInst, IDI_APPLICATION);
   wc.hCursor = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);
   wc.lpszMenuName = TEXT("");
   wc.lpszClassName = TEXT("Basic DD");
   RegisterClass(&wc);

   hWnd = CreateWindowEx(
      WS_EX_TOPMOST,
      TEXT("Basic DD"),
      TEXT("Basic DD"),
      WS_POPUP,
      0,
      0,
      GetSystemMetrics(SM_CXSCREEN),
      GetSystemMetrics(SM_CYSCREEN),
      NULL,
      NULL,
      g_hInst,
      NULL);

   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);
   SetFocus(hWnd);

   return hWnd;

}

The first thing that this function does is register a window class in windows environment (this is needed for the window creation process). In the window class we need to pass some information about the window to the RegisterClass function. All this parameters are contained in WNDCLASS structure. Notice that in many places I use the variables g_hInst. This variable will have global scope, and will hold the instance handle of our application. We will need another global variable to hold the handle of our main window (that we are about to create). To create this global variables, simply declare them above the winmain definition, like this:

HWND        g_hMainWnd;
HINSTANCE    g_hInst;

Don't forget that you need to fill the content of this variables at the very begging of your program so, at our winmain function, we'll add this code:

    g_hInst = hInstance;
    g_hMainWnd = InitWindow(nCmdShow);

    if(!g_hMainWnd)
        return -1;

Notice that we are assigning the result of our InitWindow function to the main window global variable, because this function will return a handle to our newly created window. There's an extra information at the window creating function that we haven't discussed yet, the lpfnWndProc. In this parameter we need to assign a reference to a function that will be our main window procedure. This procedure is responsible for receiving the messages that Windows sends to our application. This function will be called by the system (not by you) every time your application receives a message (like a key pressed, a painting message, a mouse move and so on...). Here is the basic definition of our WndProc function:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM 
lParam)
{
   switch (message)
   {
   case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

   } // switch

   return DefWindowProc(hWnd, message, wParam, lParam);
} //

Ok, our windows application is almost set, we are only missing an important code: the message loop. In order to allow windows to send messages to our program, we need to call a function to check if our program has received any messages. If we receive this messages we need to call a function so that our WndProc can process the message. If we didn't receive any system message, we can use this "spare time" of our application to do some background processing and even do some DirectX stuff. This process is called Idle Processing. We need to insert our message loop right after the initialization of our global variables.

while( TRUE )
{
    MSG msg;

    if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        // Check for a quit message
        if( msg.message == WM_QUIT )
            break;

        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        ProcessIdle();
    }
}

In our message loop, the first thing we do is check the message queue for messages to our application. This is accomplished by calling the PeekMessage function. If the function returns true, we call TranslateMessage and DispatchMessage so that the messages received by our program are processed. If we have no message, we'll call another function called ProcessIdle. This function will be created in our program and we'll use it to update the graphics of our screen. Here is a simple definition of the function:

void ProcessIdle()
{

}

Ok, our basic windows application is set. If you compile and run the application you will see an entirely black window that covers all your desktop.

Initializing DirectX and DirectDraw

Now we are going to work on the initialization of the DirectDraw in our application. Before your start to modify the code, I need to present you some concepts (surfaces and page flipping). All the drawing created by DirectDraw are based on structures called surfaces. Surfaces are memory regions that contains graphics that can be used in your application. Everything we need to drawn on the screen needs to be created on a surface first. Let's assume that we are creating a space invaders game ( like the one I wrote). For this you'll probably need a graphic buffer that will hold the space ships, the UFOs, the shots.

All this graphics will be stored in memory in this structures that we'll call surfaces. In fact, for DirectDraw applications, the area that displays what we are seeing on the screen is considered a surface too, and it's called the FrontBuffer. Attached to this FrontBuffer surface, we have another surface called the BackBuffer. This surface stores the information of what will be showed to the user in the next frame of our application. Lets say that the user is currently seeing an UFO on the screen at position (10,10) and the user's ship is at position (100,100). Since the objects are moving, we need to move our UFO to the position (12,10) and our ship to position (102, 10). If we draw this to the front buffer directly we can have some kind of synchronization problems (ie. the user can see the UFO move first and them the ship, but they need to move both at the same time). To solve this we draw everything we need to show to the user in the next frame in the backbuffer. When we finish it, we move all the information contained in the backbuffer to the frontbuffer. This process is called page flipping and is very similar to the process of creating cartoons (where we use lots of paper sheets to animated a drawing).

What really happens in the background is that DirectDraw changes the pointer of backbuffer with the pointer of frontbuffer, so that next time the video card send the video data to the monitor it uses the backbuffered content and not the old frontbuffer. When we do a page flip, the content of the backbuffer becomes the content of the previously showed frontbuffer, and not the same content of the drawn backbuffer as you might think.

Now that you have some idea of the concepts of DirectDraw, we will start coding the DirectX part of the program. The first thing you need to do is insert the #include of DirectDraw in your main source file. Just insert the line below in the top of your file.

#include <ddraw.h>

You need to inform the library files related to DirectDraw too. Go to the Project Menu, submenu Settings. Select the link tab and put the following lib files in the "Object/Library Modules"

kernel32.lib user32.lib ddraw.lib dxguid.lib gdi32.lib

Now we are going to create a new function in our program. This function will be called InitDirectDraw and it will be used to start the main DirectDraw object and create the main surfaces that we are going to use (the front and back buffer surfaces).

Collapse
int InitDirectDraw()
{
   DDSURFACEDESC2 ddsd;
   DDSCAPS2       ddscaps;
   HRESULT          hRet;

   // Create the main DirectDraw object.
   hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, 
                             NULL);
   if( hRet != DD_OK )
       return -1;

   // Get exclusive mode.
   hRet = g_pDD->SetCooperativeLevel(g_hMainWnd, 
                                     DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
   if( hRet != DD_OK )
       return -2;

   // Set the video mode to 640x480x16.
   hRet = g_pDD->SetDisplayMode(640, 480, 16, 0, 0);
   if( hRet != DD_OK )
       return -3;

   // Prepare to create the primary surface by initializing
   // the fields of a DDSURFACEDESC2 structure.
   ZeroMemory(&ddsd, sizeof(ddsd));
   ddsd.dwSize = sizeof(ddsd);
   ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
   ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |
      DDSCAPS_COMPLEX;
   ddsd.dwBackBufferCount = 1;

   // Create the primary surface.
   hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSFront, NULL);
   if( hRet != DD_OK )
       return -1;

   // Get a pointer to the back buffer.
   ZeroMemory(&ddscaps, sizeof(ddscaps));
   ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
   hRet = g_pDDSFront->GetAttachedSurface(&ddscaps, &g_pDDSBack);
   if( hRet != DD_OK )
       return -1;

   return 0;

} 

Notice that in this function we are using some other variables with the "g_" (global) prefix. Since we are going to use the backbuffer and front buffer reference through all our code, we are goinf to store this two surface handles in global variables. The other variable that we are storing as global is the main DirectDraw object (g_pDD). This object will be use to create all the DirectDraw related objects. So, at the top of our code, add the following global variables.

LPDIRECTDRAW7        g_pDD = NULL;        // DirectDraw object
LPDIRECTDRAWSURFACE7 g_pDDSFront = NULL;  // DirectDraw fronbuffer surface
LPDIRECTDRAWSURFACE7 g_pDDSBack = NULL;   // DirectDraw backbuffer surface

Now lets get back to our InitDirectDraw function. The first thing we are doing at the function is the creation of the DirectDraw object. To create this object we use the DirectDrawCreate function that is defined in the ddraw.h header. There are two important parameters in this function, the second and the third one. The second parameter passes a reference to the variable where we want to store the DirectDraw object variable (in our case the g_pDD variable). In third parameter we need to pass the version of DirectDraw object we are trying to get. This allow you to work with older version of DirectDraw even if you install the new version of the SDK. In my case I´m using the objects of DirectX 7, but using DX SDK 8.1.

Notice that I´m testing the result of the function for DD_OK, that is the Ok result for all DirectDraw functions. It´s important to test every return code of the DirectDraw function calls. If we receive a value different from DD_OK we return a negative value to the function. If you have some error at this point of the program you can assume that the user probably doesn't have the correct version of DirectX installed on his computer, so you can give him some friendly message (we will see this later).

The second function call is the SetCooperativeLevel. This function is used to tell DirectX how we are going to work with the display, if we are going to use full-screen mode or windowed mode and some other options. You can check the available options at the DirectX documentation. We test the result of this function as we have done in the first function call.

The third function called is the SetDisplayMode. This function is responsible for selecting the resolution we are going to use with our application. In this case we are creating an 640x480 full screen. The third parameter represent the color depth that we are using. That will depend on the number of colors you want to use with your app.

After starting the display, we need to create the two surfaces that we will use to draw our graphics on the screen. First we need to initialize the front buffer (the one that the user is seeing). When we want to create a surface with DirectDraw we need to initialize the DDSURFACEDESC2 structure that have some parameter for the creation of the surface. Its important to cleanup the structure first with a ZeroMemory or memset (or you can have some problems in some calls). Since we are creating the front buffer we need to fill the dwflags parameter with the value DDSD_BACKBUFFERCOUNT, so that the creation function recognizes that out frontbuffer will have an associated backbuffer. At the ddsCaps.dwCaps parameter we need to inform that we are creating the front buffer surface (or primary surface) with the DDSCAPS_PRIMARYSURFACE parameter. Since we are going to work with a flipping surface, we need to inform the DDSCAPS_FLIP and the DDSCAPS_COMPLEX parameters.

After setting up the DDSURFACEDESC2 structure we need to call the CreateSurface function from our DirectDraw global object, passing as parameter the surface description structure and the global object that will hold the DirectDraw frontbuffer surface.

After creating the frontbuffer surface we need to get the backbuffer associated with this frontbuffer. We can do that by calling the GetAttachedSurface of the frontbuffer surface. As a parameter we need to pass a DDSCAPS2 structure, so that the function know that we are trying to get the backbuffer.

Now that our function is created, we need to call it from the main function. Here is how we are going to call it:

if(InitDirectDraw() < 0)
{
    CleanUp();
    MessageBox(g_hMainWnd, 
               "Could start DirectX engine in your computer." 
               "Make sure you have at least version 7 of "
               "DirectX installed.", 
           "Error", MB_OK | MB_ICONEXCLAMATION);
    return 0;
}

Notice that we are testing for a negative result. If we receive a negative result we tell the user that he probably didn´t installed the correct version of DirectX.

We have an extra function call here, the Cleanup function. The Cleanup function will be responsible for deleting all the objects created by DirectX. All the objects are destroyed by calling the Release method of each instance. Here is the function definition:

void CleanUp()
{
    if(g_pDDSBack)
        g_pDDSBack->Release();

    if(g_pDDSFront)
        g_pDDSFront->Release();

    if(g_pDD)
        g_pDD->Release();
}

Before we compile and run the code again, insert the following code to the WndProc function, at the switch statement that handles the messages.

   case WM_KEYDOWN:
       if(wParam == VK_ESCAPE)
       {
            PostQuitMessage(0);
            return 0;
       }
       break;

With this code you'll be able to get out of the application by pressing the ESCAPE key. Now, compile and run the application and notice that you'll enter in the 640x480 fullscreen mode.

Blitting Graphics

Now we are going to draw some things in our backbuffer so that we can flip the surfaces and produce some animation. We are going to use a bitmap with some tiles of a race car that produce an animation. To create an sprite in DirectDraw we need to store this bitmap in another surface (that we will call tile or offscreen surface) so that we can blit (print) this surface in the backbuffer and produce the animation. We are going to create a class called cSurface to help us to manage our tile surfaces. Right click in the ClassView of Visual C++ and select the Create New Class option. As class type, select Generic Class and for the name use cSurface.

Let´s start by creating the member variables of our class. The main variable will be of LPDIRECTDRAWSURFACE7 type and will hold a reference to the DirectDraw Surface object associated with our class. We are going to store the width and height of our surface too. We will have another member called m_ColorKey that I´ll explain later. Here is the definition of our member variables.

protected:
    COLORREF m_ColorKey;
    UINT m_Height;
    UINT m_Width;
    LPDIRECTDRAWSURFACE7 m_pSurface;

The first function we are going to insert in our class is the Create function. This function will be used to create the DirectX surface object for our bitmap. Here is the Create function code:

Collapse
BOOL cSurface::Create(LPDIRECTDRAW7 hDD, int nWidth, int nHeight, 
                      COLORREF dwColorKey)
{
    DDSURFACEDESC2    ddsd;
    HRESULT        hRet;
    DDCOLORKEY          ddck;


    ZeroMemory( &ddsd, sizeof( ddsd ) );

    ddsd.dwSize = sizeof( ddsd );
    ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
    
    ddsd.dwWidth  = nWidth;
    ddsd.dwHeight = nHeight;

    hRet = hDD->CreateSurface(&ddsd, &m_pSurface, NULL );
    if( hRet != DD_OK )
    {
        
    if(hRet == DDERR_OUTOFVIDEOMEMORY)
    {
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN |
                  DDSCAPS_SYSTEMMEMORY;
    
    hRet = hDD->CreateSurface(&ddsd, &m_pSurface, NULL );
    }

    if( hRet != DD_OK )
    {
        return FALSE;
    }
    }


    if((int)dwColorKey != -1)
    {
    ddck.dwColorSpaceLowValue = dwColorKey;
    ddck.dwColorSpaceHighValue = 0;
    m_pSurface->SetColorKey(DDCKEY_SRCBLT, &ddck);
    }

    m_ColorKey = dwColorKey;
    m_Width  = nWidth;
    m_Height = nHeight;

    return TRUE;
}

Notice that the creation process used to create the tile surface is very similar to the creation process of the front buffer surface. The different is at the information assigned to the DDSURFACE2 structure. At the dwFlags parameter we inform that the dwCaps, dwWidth and dwHeight will have information that needs to be used to create the surface. In the dwCaps parameter we inform that this surface is and offscreen surface (tile surface) by using the DDSCAPS_OFFSCREENPLAIN flag. We combine with this value with the DDSCAPS_VIDEOMEMORY value, that tells the function that we are trying to create this function in video memory.

At the error test we are testing if the return value of the function is DDERR_OUTOFVIDEOMEMORY so that if the user has an old video card if just a few MB of memory, we can change the DDSURFACEDESC2 parameter to DDSCAPS_SYSTEMMEMORY and try to create the surface on RAM instead of VIDEO memory. The process of blitting surfaces from SYSTEM_MEMORY to VIDEO_MEMORY is much slower then the VIDEO MEM to VIDEO MEM process but is needed in case of the user doesn´t have enough memory.

At the last portion of the function we have the dwColoKey parameter test. This is used if we are working with a colorkeyed surface. A colorkeyed surface is a surface where we don't want to display a certain color. Let's say that I want to blit a spaceship in a starfield background. When I blit the spaceship I don't want to display the black background of the bitmap just the ship itself so I can associate a color key to the ship to display just the ship picture and not the background. You need to take care when you create your tile bitmaps and make sure to not use antialised backgrounds in the sprite bitmaps (lots of application allows you to removed the antialiased background so that you can have high quality sprites).

Now we will create another function to load a bitmap file into the DirectX surface object. For this we are going to use some basic GDI functions. Since we are going to load this just once, this will probably not impact much on the performance of the drawing process. Here is the LoadBitmap function:

Collapse
BOOL cSurface::LoadBitmap(HINSTANCE hInst, UINT nRes, int nX, int nY, 
                          int nWidth, int nHeight)
{
    HDC                     hdcImage;
    HDC                     hdc;
    BITMAP                  bm;
    DDSURFACEDESC2          ddsd;
    HRESULT                 hr;

    HBITMAP    hbm;

    hbm = (HBITMAP) LoadImage(hInst, MAKEINTRESOURCE(nRes), 
                              IMAGE_BITMAP, nWidth, nHeight, 0L);

    if (hbm == NULL || m_pSurface == NULL)
        return FALSE;

    // Make sure this surface is restored.
    m_pSurface->Restore();

    // Select bitmap into a memoryDC so we can use it.
    hdcImage = CreateCompatibleDC(NULL);
    if (!hdcImage)
        return FALSE;

    SelectObject(hdcImage, hbm);

    // Get size of the bitmap
    GetObject(hbm, sizeof(bm), &bm);

    if(nWidth == 0)
    nWidth = bm.bmWidth;
    
    if(nHeight == 0)
    nHeight = bm.bmHeight;
    
    // Get size of surface.
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_HEIGHT | DDSD_WIDTH;
    m_pSurface->GetSurfaceDesc(&ddsd);

    if ((hr = m_pSurface->GetDC(&hdc)) == DD_OK)
    {
        StretchBlt(hdc, 0, 0, ddsd.dwWidth, ddsd.dwHeight, hdcImage, 
                   nX, nY, nWidth, nHeight, SRCCOPY);
        m_pSurface->ReleaseDC(hdc);
    }
    DeleteDC(hdcImage);

    m_srcInfo.m_hInstance = hInst;
    m_srcInfo.m_nResource = nRes;
    m_srcInfo.m_iX          = nX;
    m_srcInfo.m_iY          = nY;
    m_srcInfo.m_iWidth    = nWidth;
    m_srcInfo.m_iHeight      = nHeight;
    
    return TRUE;
}

This function is very easy to understand if you know a little bit of GDI programming, anyway I'll explain all the code. The first thing we need to do is to call the restore method of our m_Surface internal variable. This will restore the memory allocated to the surface object of DirectDraw, in case DirectDraw deallocates the memory (if this situation happens, any function call referring the m_Surface object will return DERR_SURFACELOST). After restoring the memory we create a GDI dc and load the bitmap passed as a parameter from the resource. The bitmap is them selected in the DC and blittled in the surface using StretchBlt function. Notice that I'm saving the bitmap information in a m_srcInfo structure. This structure is used when we have a surface lost problem, this way we can restore the surface with its original data.

The last function we are going to present here is the Draw function that is used to Draw a portion of the surface in to another surface. In most of the cases you´ll draw the surface in the backbuffer but you can use this Draw method with any other kind of surface.

Collapse
BOOL cSurface::Draw(LPDIRECTDRAWSURFACE7 lpDest, int iDestX, int iDestY, 
                    int iSrcX, int iSrcY, int nWidth, int nHeight)
{
    RECT    rcRect;
    HRESULT    hRet;

    if(nWidth == 0)
        nWidth = m_Width;

    if(nHeight == 0)
        nHeight = m_Height;

    rcRect.left   = iSrcX;
    rcRect.top    = iSrcY;
    rcRect.right  = nWidth  + iSrcX;
    rcRect.bottom = nHeight + iSrcY;

    while(1)
    {
        if((int)m_ColorKey < 0)
        {
            hRet = lpDest->BltFast(iDestX, iDestY, m_pSurface, 
                                   &rcRect,  DDBLTFAST_NOCOLORKEY);
        }
        else
        {
            hRet = lpDest->BltFast(iDestX, iDestY, m_pSurface, 
                                   &rcRect,  DDBLTFAST_SRCCOLORKEY);
        }

        if(hRet == DD_OK)
            break;

        if(hRet == DDERR_SURFACELOST)
        {
            Restore();
        }
        else
        {
            if(hRet != DDERR_WASSTILLDRAWING)
                return FALSE;
        }
    }

    return TRUE;

This function is extremely simple. The first thing we do is create a rect variable and fill it with the source bitmap position and size that we want to blit in the destination surface. After that, we call the BltFast method of the surface to blit the content in the destination surface. Notice that we're making a test to see if the surface have a color key or not. Blitting surfaces without colorkey is much faster than surfaces that have colorkey, so create colorkey only when needed. You can see that the drawing code is inside and infinite loop. This is created because the drawing function can return a surface lost error. If this error is returned we need to restore the surface and try to blit it again until we got the surface restored.

Another important function is the Destroy function that is responsible for releasing the DirectDraw resources related to this objects. Its basically a call to the Release method of the m_Surface variable.

void cSurface::Destroy()
{

    if(m_pSurface != NULL)
    {
        m_pSurface->Release();
        m_pSurface = NULL;
    }
}

In the source code you´ll find some other methods in this class but basically, for this article, you'll only need this four. Compile the code to see if you have no errors.

Drawing in the BackBuffer using the cSurface Class

The next step is the creation of an instance of our cSurface class so that we can blit this information on the backbuffer. To do this, we need to insert an include statement in the file that contains out WinMain function.

#include "csurface.h"

After including the header of our class, create a new global variable that will hold our instance. You can create it below the declaration of the other global variables.

cSurface    g_surfCar;

After proceeding with the coding, add the bitmap resource to the object so that we can use it to blit the surface in the backbuffer. The resource is a bitmap file called bmp_bigcar_green.bmp . This bitmap is used in my new game (RaceX) that will be posted here in CP pretty soon. You can create a resource ID for the bitmap with the "IDB_GREENCAR" name.

Now that we have the surface class instance declared, we need to call the create and loadbitmap method to create the DirectXobject inside the class. This code can be inserted after the call of InitDirectDraw.

g_surfCar.Create(g_pDD, 1500, 280);
g_surfCar.LoadBitmap(g_hInst, IDB_GREENCAR, 0, 0, 1500, 280);

Before we proced, remember that you need to destroy this object in the case you create it during the code execution. For this you need a call to the Destroy method. You can put this in the CleanUp function.

void CleanUp()
{
    g_surfCar.Destroy();

    if(g_pDDSBack)
        g_pDDSBack->Release();

    if(g_pDDSFront)
        g_pDDSFront->Release();

    if(g_pDD)
        g_pDD->Release();
}

Now that we have created, initialized and added the destruction code of our surface class we just need to draw the picture in the backbuffer and flip the surface in the ProcessIdle function.

void ProcessIdle()
{
    HRESULT hRet;

    g_surfCar.Draw(g_pDDSBack, 245, 170, 0, 0, 150, 140);

    while( 1 )
    {
        hRet = g_pDDSFront->Flip(NULL, 0 );
        if( hRet == DD_OK )
        {
            break;
        }
        if( hRet == DDERR_SURFACELOST )
        {
            g_pDDSFront->Restore();
        }
        if( hRet != DDERR_WASSTILLDRAWING )
        {
            break;
        }
    }
}

This code draws the first picture of the car in the middle of the backbuffer and flips the backbuffer with the front one every time we have an idle processing call. Let´s change the code a little bit so that we can blit the animation of the car.

Collapse
void ProcessIdle()
{
    HRESULT hRet;
    static int iX = 0, iY = 0;
    static iLastBlit;

    if(GetTickCount() - iLastBlit < 50)
    {
        return;
    }

    g_surfCar.Draw(g_pDDSBack, 245, 170, iX, iY, 150, 140);

    while( 1 )
    {
        hRet = g_pDDSFront->Flip(NULL, 0 );
        if( hRet == DD_OK )
        {
            break;
        }
        if( hRet == DDERR_SURFACELOST )
        {
            g_pDDSFront->Restore();
        }
        if( hRet != DDERR_WASSTILLDRAWING )
        {
            break;
        }
    }

    iX += 150;
    if(iX >= 1500)
    {
        iX = 0;
        iY += 140;
        if(iY >= 280)
        {
            iY = 0;
        }
    }

    iLastBlit = GetTickCount();
}

We create 3 static variables. The first 2 will be used to change the position of the blitted portion of the source bitmap. This way we can create the animation of the car by going from frame 1 to frame 20. Notice that we have an extra variable called iLastBlit that holds the result of a GetTickCount function call. This is used to allow that each frame stays on the screen at least 50 miliseconds, this way the animation will go pretty smooth. You can remove this code to see what happens (on my machine the car spins too fast).

This was a brief introduction on how to create a basic C++ program that uses the DirectX DirectDraw library. If you have any questions or comments just post them in! 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值