Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

.NET

Writing Windows CE Display Drivers


Sep00: Writing Windows CE Display Drivers

Simplifying and speeding up an intimidating task

Jeff is a Windows CE program manager at Intelligraphics, where he develops Windows CE device drivers. He can be reached at [email protected].


In general, writing display drivers can be an intimidating task, and developing them under Windows CE is no exception. Fortunately, Microsoft does provide C++ classes that simplify the project. But display-driver development can be further simplified while, at the same time, display drivers are made more portable across Windows CE devices.

Increased portability provides additional value for display drivers as Windows CE moves to different platforms with various display requirements such as Internet appliances, video-conferencing systems, and game consoles. Even within a product family, such as hand-held PCs (HPCs), this portability is valuable as each generation adds more features and functionality and moves to different display hardware.

In this article, I'll examine the fundamentals of Windows CE display driver development, including the display-driver C++ classes. I will also explore improvements to the C++ classes that will simplify display-driver development. In the end, you will have a set of improved display- driver classes that should be simple enough to quickly get running on any device, yet complete enough to support any additional hardware features. Of course, this doesn't prevent you from making your own improvements as well.

Display-Driver Overview

The Graphics Device Interface (GDI) is the system component that loads and calls display drivers. It is also where bit-block transfers (blits) and drawing related Win32 APIs are handled. In Windows CE, GDI is contained in the Graphic, Windowing, and Event Subsystem (GWES). GWES is an optional component in CE, making the display driver optional as well. It is, therefore, possible to make Windows CE devices that are headless -- that is, without graphic display interfaces. This is important when writing other device drivers because you may not be able to depend on having access to graphical objects (such as dialog boxes) to communicate with users.

Display drivers are native drivers in Windows CE. This means that it has a custom interface that provides a standard set of functionality for display devices. As a matter of fact, this display device driver interface (DDI) is a subset of the Windows NT DDI. One of the major differences is that all Windows CE display drivers have the same level of functionality, so there is no punting of complex operations back to GDI. A Windows CE display driver has only one function it is required to export -- DrvEnableDriver(). This function is called by GDI when the driver is loaded and is responsible for returning pointers to the other DDI functions.

Writing display drivers using the DDI directly can involve a lot of duplication of code from one display driver to the next. To simplify and speed development, Microsoft provides C++ classes that contain most of the base code required of any display driver. This means that code only needs to be added for functionality that is specific to the display controller, such as initialization and mode setting, hardware cursors, and accelerated blits and line drawing. You can appreciate how much time these classes save if you have ever done display-driver development for other Windows systems. In a typical Windows CE display driver, the most time-consuming tasks usually involve device initialization and mode setting. This is because CE does not support video BIOS calls, where initialization and mode setting are normally handled. The one exception to this is the CEPC platform, where the bootloader (LOADCEPC) makes video BIOS calls to initialize and set the mode for the display device. This is possible because LOADCEPC is actually a DOS application. However, for the typical Windows CE system, this is not an option.

Microsoft makes several recommendations for display hardware, the most important being that the display hardware uses a linear frame buffer. This means that the display memory needs to be contiguous, with this entire block of memory being directly accessible by the CPU. Therefore, access to display memory through bank selection should not be used. Another important recommendation is not to use bit planes. This is when each color channel or intensity component is in a separate buffer. Display memory bank selection and bit planes exist as part of legacy VGA support, while most newer VGA controllers also provide support for a linear frame buffer as well as for packed pixel, or nonplanar, display modes. It is important to follow these recommendations if you wish to take advantage of the default blit and line-drawing functions in the C++ classes.

Graphics Primitive Engine Classes

The Graphics Primitive Engine (GPE) simplifies display-driver development in Windows CE. The most important of these is the GPE class, which represents the display device. It is a pure virtual class, which must be derived from and certain base class functions must be implemented. Since source code is not provided in Platform Builder for this class or how the DDI functions interface with it, it is useful to review these functions that require implementation. They are:

  • NumModes. Returns the number of display modes supported by the display driver.
  • GetModeInfo. Returns information about a specific display mode, such as display width and height in pixels and number of bits per pixel. This function should handle the number of display modes returned by the NumModes() function. The first mode entry in the list of supported modes is always the one selected for configuration when SetMode() is later called.

  • SetMode. Sets the display mode. This can be the most time-consuming function to write in the entire driver, especially for VGA controllers, since configuration of the display device can be a fairly involved process.

  • AllocSurface. Allocates a surface, which is just a block of system or video memory to store pixel data. The GPESurf class can be used to represent surfaces in system memory. To represent surfaces in video memory, GPESurf must be derived from.

  • SetPointerShape. Sets the cursor bitmap and cursor hotspot.

  • MovePointer. Moves the cursor.

  • BltPrepare. Called before the blit operation is performed. It lets the driver set up the blit hardware for performing the operation, if it is supported. It must return the actual function to be called to perform the blit operation, which can be the default blit function provided in the GPE class.

  • BltComplete. Called after the blit operation has been performed. It lets the driver do any cleanup necessary after the blit operation.

  • Line. Called before and after a line-drawing operation. When it is called before the line drawing is done, the function can set up the line-drawing hardware for performing the operation, if it is supported. It must return the actual function to be called to perform the line-drawing operation, which can be the default line-drawing function in the GPE class. When it is called after the line drawing is done, the function can do any cleanup necessary after the line-drawing operation.

  • SetPalette. Sets the palette. This only applies for modes that support a palette, which is typically 8 bits per pixel or less.

  • InVBlank. Indicates if the display update is in the vertical blanking period. This is useful for reducing an animation problem known as "tearing," where the display memory update is not in sync with the display refresh on the monitor.

There are other functions in the GPE class that have default implementations, but can be overridden for nondefault behavior. These functions are:

  • IsPaletteSettable. Returns True if the palette is settable or False if the palette is fixed or if the mode doesn't require a palette, such as 16-bit or 24-bit per pixel modes. The default implementation checks the Bpp field of the m_ pMode variable and returns True if it equals eight, otherwise it returns False.
  • GetGraphicsCaps. Indicates if the display driver supports additional graphics features. The default implementation returns 0.

  • ContrastControl. Receives commands related to contrast control, such as getting and setting the current contrast value. It is useful if the display is an LCD panel. The default implementation does nothing and always returns 1.

  • PowerHandler. Receives power on/off notification. The function can be used to suspend or resume power to the display device. As with any driver that receives power notifications, it is important to note that this function should not make any calls that could cause the thread to block as it is the only thread running at the time it is called.

There is also a set of functions in the GPE class that are described as being required for DDHAL support. The implication is that these functions are part of the DirectDraw Hardware Abstraction Layer (DDHAL). Since the display-driver model for Version 2.0 was developed before the release of DirectDraw for Windows CE, these functions were probably included as a best guess of the display-driver requirements for DirectDraw support. As it stands, these functions are only part of the story for DirectDraw support and are not required for a standard display driver, so I will not specifically discuss them here. These functions do provide additional capabilities that, if properly exported, could be used by applications to bypass GDI for improved performance.

If your display device is VGA based, you may choose to derive your device class from the GPEVGA class rather than GPE. GPEVGA is itself derived from GPE, and is still pure virtual. It contains function implementations for SetPalette() and InVBlank(), using standard VGA accesses. It also contains I/O mappings to standard VGA registers. Beyond this, you still need to implement the remaining functionality required in the GPE base class.

The "dirty rectangle driver" is a variation of the GPE-based driver. Its purpose is to let a display driver based on the GPE class work with a banked memory display device. An example of this is the sample driver VGA8BPP, an inefficient driver from both a system resource and performance standpoint that should really only be used if a linear frame buffer is not available. It works by creating a frame buffer in system memory that is the size of the display. This is inefficient from a system memory standpoint since memory is usually a critical resource in a Windows CE device. All drawing by the GPE default drawing functions is done into this system memory frame buffer. These areas where drawing is done (the dirty rectangles) are then copied to the display memory on a bank- by-bank basis. Doing the drawing in two steps is obviously inefficient.

Another important class is GPESurf, which is used to represent surfaces located in system memory. If the driver supports the creation of surfaces in video memory, then the GPESurf class will need to be derived. However, these changes are fairly minor and mostly require capturing of the Node2D information (mentioned later) and setting the flag to indicate that this surface is located in video memory.

Finally, the Node2D class is used for managing video memory surfaces. An instance of a Node2D object is initially created that contains the pixel dimensions of the total video memory. Allocation of video memory for surfaces, including the primary display surface, is then handled through this Node2D instance. Within the class itself, the video memory is managed as a list of free and allocated rectangles, with all coordinates handled in pixel dimensions. However, this class has some limitations. The first one is that video memory surfaces cannot be created wider than the pixel dimensions supplied when the instance of the class was created. Another limitation is that video memory surfaces must be the same color depth as the display.

GPE classes haven't changed much since Version 2.0, so the information presented here should be useful and accurate across many different versions of Windows CE.

Improving the Display-Driver Classes

As useful as these C++ classes are for writing Windows CE display drivers, additional base class functionality could be included to further simplify display-driver development. The two classes to be improved are GPE and GPESurf. Because the source for these classes is not provided, we need to derive classes from them to improve them. These new classes are called NewGPE and NewGPESurf. The source files for the NewGPE and NewGPESurf classes are available electronically from DDJ (see "Resource Center," page 5) and from http://www .intelligraphics .com/. All the code provided was taken from and developed and tested with Microsoft Window CE Platform Builder 2.12. Any differences that may exist among different flavors of CE were not taken into consideration, except where noted.

A good starting point for building the NewGPE class is configuration and initialization. The NewGPE class is designed to require as little modification or overriding of functions as possible. The class constructor, NewGPE(), provides default initialization for the variables. Currently, the only variable that would need to be changed here is m_bIsVGADevice, if a nonVGA device is being used. This variable is used to enable mapping of the VGA registers as well as to select some default VGA functionality, which are both performed elsewhere in the driver. Another option is to set these variables based on registry values or data in a configuration file; see Listing One.

To allow these functions to be hardware independent, mode information used by NumModes() and GetModeInfo() (see Listing Two) is taken from the variable m_gpeModeTable, a table of the supported display modes. This variable needs to be initialized by the driver developer based on the modes that will be supported by the driver. It is statically defined in the provided source file NEWGPE .CPP. However, this could be changed, so that the table is instead loaded from the registry or a configuration file when the driver is first run.

SetMode() has been written to require no additional modification. It handles all the hardware-independent mode initialization. The first step is to verify the selected mode from the list of supported modes. Once this is complete, the hardware-dependent mode initialization function, ModeInit(), is called. The ModeInit() function must be overridden or modified to initialize the specific hardware and set up certain variables used elsewhere in the initialization process. These variables are:

  • m_nLAWPhysical. Specifies the physical address of the linear access window of the display device for accessing the frame buffer.
  • m_nLAWSize. The size of the linear access window.

  • m_nVideoMemorySize. The total amount of video memory available.

  • m_nVideoMemoryStart. The offset of video memory in the linear access window. This is usually 0.

  • m_nScreenStride. The number of bytes per display line.

  • m_bHWCursor. Flags to indicate support for hardware cursors, which are used to bypass existing software cursor support. The actual implementation for hardware cursors still would need to be included.

  • m_b555Mode. Flags to indicate that the 16-bit color mode uses 5 bits each for red, green, and blue.

  • m_bIsVGADevice. Flags to indicate that the device is VGA based. This should actually be cleared in the constructor to prevent mapping of VGA registers, but at this point it is still useful in bypassing default VGA implementations for SetPalette() and InVBlank().

Once the hardware-specific initialization is completed in ModeInit(), the SetMode() function continues the hardware-independent mode initialization. The m_pMode, m_nScreenWidth, and m_nScreenHeight variables, used by other functions, are set for the new display mode. Then a pointer, m_pLAW, to be used later for accessing video memory, is mapped to the physical address of the linear access window of the display device. To do this, calls to the VirtualAlloc() and VirtualCopy() functions are required. Next, a Node2D object, m_p2DVideoMemory, representing all of available video memory, is created. This Node2D object manages video memory for the AllocSurface() function, which is then called to create the primary display surface, m_pPrimarySurface. Finally, the bit mask is initialized and the default palette is set up. It may not be clear within this function, but there are really two different 16-bit display modes in common use. One mode uses 5 bits each for red, green, and blue, while the other mode uses 5 bits for red and blue and 6 bits for green. The mask values are used by GDI to generate the correct pixel value to represent a particular color. The flag m_b555Mode is used in the driver to determine which 16-bit display mode has been set in the hardware when initializing the mask values. For modes other than 16 bit, this flag is ignored. The bit mask created here, m_ulBitMasks, is also used by the DDI function DrvGetMasks() to return pixel formatting information; see Listing Three.

The base surface class, GPESurf, must be derived to support the creation of video memory surfaces. Since default management of video memory is already defined within the NewGPE class through the Node2D object m_p2DVideoMemory, it makes sense to include support for the creation of video memory surfaces in the derived surface class, NewGPESurf. This is accomplished by providing a constructor that sets the video memory flag and maintains the Node2D instance for the video memory of this surface. The destructor is then responsible for deleting the saved Node2D instance, thus freeing the video memory occupied by this surface. Listing Four shows all that needs to be done for the NewGPESurf class.

The AllocSurface() function in the NewGPE class (see Listing Five) is responsible for handling the creation of system and video memory surfaces. It uses m_p2DVideoMemory, the Node2D object previously created in the SetMode() function, for allocating video memory. The surface itself is then created using the derived NewGPESurf class. As previously noted, video memory surfaces can only be created in the same pixel format as the current display mode due to limitations in Node2D. System memory surfaces are created using the base GPESurf class.

The most straightforward places to provide default implementations are the line- drawing and blit functions. For BltPrepare(), all that needs to be done is to specify the default GPE blit function. For BltComplete(), nothing needs to be done; see Listing Six. As with BltPrepare(), the function Line() (Listing Seven) can simply specify the default GPE line-drawing function.

For display devices that handle cursors in hardware, the cursor functions SetPointerShape() and MovePointer() need to be changed or overridden to include this support. Since this functionality is not available in all display hardware or only in certain display modes, I have provided software-cursor support in the base class implementation of these functions. See the provided source file CURSOR.CPP for the details of software-cursor support. It should be noted that software-cursor management requires changes in other sections of the driver as well. Searching the provided source files for the variable m_bHWCursor, the flag for selecting between software and hardware cursor, will highlight these additional changes.

For SetPalette() and InVBlank(), the VGA implementations are well defined and are even provided in the GPEVGA class. Therefore, default VGA support is included in the NewGPE class as well. For nonVGA display devices, support will have to be added to these functions. For reference, see the implementation of these functions in the provided source file MISC.CPP.

As you have seen, the NewGPE class requires only the ModeInit() function to be modified or overridden when using a VGA display device that supports a linear frame buffer. For nonVGA display devices, the only additional functions that need to be modified are SetPalette() and InVBlank(). In addition, for any display-device type, the m_gpeModeTable variable needs to be initialized with the modes supported by the driver. It would be difficult to make display-driver development any simpler than this.

Conclusion

As you have seen, the Windows CE display-driver model and the Microsoft-provided C++ classes are a good starting point for display-driver development. The improvements made here to these C++ classes can further simplify this development process, while still allowing the same level of flexibility provided in the original classes. As well, these changes should make the classes more portable as you go from project to project. However, it is important to keep in mind that this discussion is by no means comprehensive in its coverage of display-driver-related development in Windows CE, and that there are plenty of other topics for you to explore.

DDJ

Listing One

NewGPE::NewGPE()
{
    // Flags for hardware features
    m_bIsVGADevice = TRUE;      // default to VGA device
    m_bHWCursor = FALSE;        // default to software cursor
    m_b555Mode = FALSE;         // default to 5-6-5 mode for 16Bpp

    // NOTE: The following data members are modified to their final values
    // by the default implementation of SetMode and shouldn't need to
    // modified here or in ModeInit.
    // 
    m_pPrimarySurface = NULL;   // pointer to primary display surface
    m_nScreenWidth = 0;         // display width
    m_nScreenHeight = 0;        // display height
    m_pMode = NULL;             // pointer to information on current mode
    m_p2DVideoMemory = NULL;    // pointer to video memory manager
    m_pLAW = NULL;              // pointer to linear access window
    memset(&m_ulBitMasks[0], 0, sizeof(m_ulBitMasks)); // bit masks

    // NOTE: The following data members MUST be modified to their final
    // values by the display hardware specific function ModeInit.
    // 
    m_nLAWPhysical = 0;     // the physical address of the linear access
                            // window for accessing the frame buffer
    m_nLAWSize = 0;         // size of linear access window
    m_nVideoMemorySize = 0; // size of video memory, which can be different
                            // than the linear access window
    m_nVideoMemoryStart = 0;// offset within the linear access window to the
                            // start of video memory (usually is 0)
    m_nScreenStride = 0;    // number of bytes per display line
}

Back to Article

Listing Two

int NewGPE::NumModes()
{
    // count the number of entries in the mode table
    BOOL bDone = FALSE;
    int nIndex = 0;
    while (!bDone) {
        if (m_gpeModeTable[nIndex].Bpp==0) {
            // no more entries in the table
            bDone = TRUE;
        }
        else {
            // count entry and go to next entry
            nIndex++;
        }
    }
    return nIndex;
}
SCODE NewGPE::GetModeInfo(GPEMode *pMode, int modeNo )
{
    // make sure that the mode is valid (index is zero based)
    if ((modeNo<0) || (modeNo>=NumModes()))
        return E_INVALIDARG;
    // get data from mode table
    *pMode = m_gpeModeTable[modeNo];
    return S_OK;
}

Back to Article

Listing Three

NewGPE::SetMode(int modeId,HPALETTE *pPaletteHandle )
{
    int nModeNum = 0;
    GPEMode gpeMode;
    // get mode entry that matches modeId
    BOOL bDone = FALSE;
    int nNumModes = NumModes();
    while (!bDone) {
        if (nModeNum>=nNumModes) {
            // failed to find matching mode entry
            bDone = TRUE;
        }
        else if ((GetModeInfo(&gpeMode, nModeNum)==S_OK) &&
        (gpeMode.modeId==modeId)) {
            // found matching mode entry
            bDone = TRUE;
        }
        else {
            // check next entry
            nModeNum++;
        }
    }
    // check if mode number is valid
    if (nModeNum>=nNumModes) {
        return E_INVALIDARG;
    }
    // pass mode info to the hardware specific initialization function
    SCODE scInit = ModeInit(&m_gpeModeTable[nModeNum]);
    if (scInit!=S_OK) {
        // something failed here, don't go any further
        return scInit;
    }
    // verify parameters ModeInit is required to initialize
    if ((m_nLAWPhysical==0) || (m_nLAWSize==0) || (m_nVideoMemorySize==0) ||
        (m_nScreenStride==0)) {
        // ERROR: ModeInit failed to properly initialize some data
        //        members that are required. These data members need
        //        to be initialized for this function to work.
    return E_FAIL;
    }
    // continue with hardware independent initialization
    // Using the following values initialized by ModeInit:
    // * m_nLAWPhysical
    // * m_nLAWSize
    // * m_nVideoMemorySize
    // * m_nVideoMemoryStart
    // * m_nScreenStride
    // * m_bHWCursor (optional)
    // * m_b555Mode (optional)
    // Initialize the remaining values:
    // * m_pMode
    // * m_nScreenWidth
    // * m_nScreenHeight
    // * m_pLAW
    // * m_p2DVideoMemory
    // * m_pPrimanrySurface
    // * m_ulBitMasks
    // And don't forget to initialize the palette parameter pPaletteHandle.
    // initialize remaining values
    //
    m_pMode = &m_gpeModeTable[nModeNum];    // current mode
    m_nScreenWidth = m_pMode->width;    // current display width
    m_nScreenHeight = m_pMode->height;  // current display height

    // generate pointer to linear access window (can't address physical 
    // memory directly, but need to create and map a pointer)
    m_pLAW = (unsigned char *) VirtualAlloc(NULL, m_nLAWSize, 
                                               MEM_RESERVE, PAGE_NOACCESS);
    BOOL bCreateLAW;
    if (m_nLAWPhysical<0x20000000) { // handle <512MB address differently
        bCreateLAW = VirtualCopy(m_pLAW, (LPVOID) (m_nLAWPhysical|0x80000000),
    m_nLAWSize,
            PAGE_READWRITE|PAGE_NOCACHE);
    }
    else {
        bCreateLAW = VirtualCopy(m_pLAW, 
                      (LPVOID) (m_nLAWPhysical>>8), m_nLAWSize,
                        PAGE_READWRITE|PAGE_NOCACHE|PAGE_PHYSICAL);
    }
    // check for error creating LAW pointer
    if (!bCreateLAW) {
        return E_FAIL;
    }
    // create Node2D instance (for managing video memory)
    m_p2DVideoMemory  = new Node2D(m_nScreenWidth, 
                         m_nVideoMemorySize/m_nScreenStride,
                           0, 0, 32/m_pMode->Bpp);
    // check for error creating Node2D instance
    if (m_p2DVideoMemory==NULL) {
        return E_FAIL;
    }
    // create primary surface
    SCODE scAlloc = AllocSurface( &m_pPrimarySurface, m_nScreenWidth,
               m_nScreenHeight, m_pMode->format, GPE_REQUIRE_VIDEO_MEMORY);
    // check for error allocating primary surface
    if (scAlloc!=S_OK) {
        return E_FAIL;
    }
    // initialize bitmasks based on current bits per pixel
    memset(&m_ulBitMasks[0], 0, sizeof(m_ulBitMasks));
    if (m_pMode->Bpp==16) {
        // 16Bpp has two mask options
        if (m_b555Mode) {   // 5-5-5 mode
            m_ulBitMasks[0] = 0x7c00; // red
            m_ulBitMasks[1] = 0x03e0; // green
            m_ulBitMasks[2] = 0x001f; // blue
        }
        else {              // 5-6-5 mode
            m_ulBitMasks[0] = 0xf800; // red
            m_ulBitMasks[1] = 0x07e0; // green
            m_ulBitMasks[2] = 0x001f; // blue
        }
    }
    // create a default palette, if necessary
    if (pPaletteHandle!=NULL) {
        // create palette
        switch (m_pMode->Bpp) {
        case 24:
        case 32:
            *pPaletteHandle = EngCreatePalette (PAL_BGR, 0, NULL, 0, 0, 0);
            break;
        case 16:
            *pPaletteHandle = EngCreatePalette(PAL_BITFIELDS, 0, NULL, 
                         m_ulBitMasks[0], m_ulBitMasks[1], m_ulBitMasks[2]);
            break;
        case 8:
            *pPaletteHandle = EngCreatePalette(PAL_INDEXED,256,
                                        (ULONG *)m_rgbIdentityPal,0,0,0);
            SetPalette(m_rgbIdentityPal, 0, 256); 
                                        // palette needs to be set here
            break;
        case 4:
            *pPaletteHandle = EngCreatePalette(PAL_INDEXED,16,
                                        (ULONG *)m_rgbIdentityPal16,0,0,0);
            SetPalette(m_rgbIdentityPal16, 0, 16); // palette needs to be set
            break;
        default:
            RETAILMSG(1,(TEXT("NewGPE::SetMode Failed to create 
                                               unknown palette type.\r\n")));
            break;
        }
    }
    return S_OK;
}

Back to Article

Listing Four

NewGPESurf::NewGPESurf( int width, int height, void *pBits, int stride,
                EGPEFormat format, int offset, Node2D *pNode)
{
    // call general GPESurf initialization
    Init(width, height, pBits, stride, format);
    m_pNode2D = pNode;
    if (pNode!=NULL) {
        // surface in video memory, set flags and parameters
        m_fInVideoMemory = TRUE;
    }
}
NewGPESurf::~NewGPESurf()
{
    // free video memory if applicable
    if (m_fInVideoMemory && (m_pNode2D!=NULL)) {
        delete m_pNode2D;
    }
}

Back to Article

Listing Five

SCODE NewGPE::AllocSurface( GPESurf **ppSurf, int width, int height,
                EGPEFormat format, int surfaceFlags )
{
    SCODE scRet = S_OK;
    *ppSurf = NULL;

    // check parameters are valid:
    //   video memory surface must have same pixel format as display
    if (surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY) {
        // video memory surface must have same pixel format as display
        if (format!=m_pMode->format)
           return E_INVALIDARG;
    }
    // check if video memory surface requested
    if ((surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY) ||
        ((surfaceFlags & GPE_PREFER_VIDEO_MEMORY) &&
        (format==m_pMode->format))) {
        // try allocating out of video memory
        Node2D *pNode = m_p2DVideoMemory->Alloc(width, height);
        if (pNode!=NULL) {
            // get offset into video memory
            DWORD dwOffset = (m_nScreenStride * pNode->Top()) +
                ((pNode->Left() * EGPEFormatToBpp[format]) / 8);
            // now create a surface for the allocated video memory
            *ppSurf = new NewGPESurf( width, height, m_pLAW + dwOffset,
                m_nScreenStride, format, dwOffset, pNode);
            if (*ppSurf!=NULL) {
                // video memory surface allocated successfully
                return S_OK;
            }
            // failed creating surface for video memory
            delete pNode; // free video memory
        }
        // if we got here then unable to allocate out of video memory
        if (surfaceFlags & GPE_REQUIRE_VIDEO_MEMORY)
            return E_OUTOFMEMORY; // fail since surface requires video memory
    }
    // Allocate surface from system memory if not in video memory
    *ppSurf = new GPESurf(width, height, format);
    if (*ppSurf!=NULL) {
        // make sure system memory allocated
        if ((*ppSurf)->Buffer()!=NULL) {
            // system memory surface allocated successfully
            return S_OK;
        }
        // system memory surface allocation failed
        delete *ppSurf;
        // just fall through to fail
    }
    // if we got here then something failed
    return E_OUTOFMEMORY;
}

Back to Article

Listing Six

SCODE NewGPE::BltPrepare( GPEBltParms *pBltParms )
{
    // use the default blit function
    pBltParms->pBlt = EmulatedBlt;
    return S_OK;
}
SCODE NewGPE::BltComplete( GPEBltParms *pBltParms )
{
    // don't need to do anything here
    return S_OK;
}

Back to Article

Listing Seven

SCODE NewGPE::Line(GPELineParms *pLineParms,EGPEPhase phase /* = gpeSingle */)
{
    // use the default line function
    pLineParms->pLine = EmulatedLine;
    return S_OK;
}


Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.