void CReaderView::renderText() {
// position raster
glRasterPos2d(-2,-1);
// load the string (or part of) into OpenGL’s back buffer glListBase(1000); glCallLists( m_textStrAll.GetLength(), GL_UNSIGNED_BYTE, CString(m_textStrAll) ); } void CReaderView::displayText() {
// translate the view into new position float posx = float(m_pos.x)/100;
glTranslatef(posx, 0, 0);
// flush any other drawing commands glFinish();
// finally swap the buffers SwapBuffers( hDC ); } void CReaderView::scroller() { if ( m_bRenderText ) {
// Clear out the color & depth buffers firstly
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); renderText();
}
displayText();
// increment the text position
if (!m_pause) m_pos.x -= m_scrollIncrement; }
59
void CReaderView::setFont() {
// prepare the fonts & sizes for the text CFont font;
font.CreateFontIndirect(&m_logFont);
// select font into the current device context HDC hDC = wglGetCurrentDC();
SelectObject( hDC, HFONT(font));
// create the list of font glyphs for use wglUseFontBitmaps(hDC, 0, 256, 1000); // set the background (clearing) colour glClearColor(0.0F, 0.0F, 0.4F, 1.0F); m_bRenderText = true;
60
Appendix A.3
DirectDraw Based Code
CReaderView
The member m_DD refers to the instance of DDGraphics. m_bufferSize is set by calling the DDGraphics function GetBufferSize().
void CReaderView::displayText() {
CRect rSrc = m_rSrcBlt;
// check for source window moving outwith the buffer if (rSrc.right >= m_bufferSize.cx)
{
// prepare to render used-up surface m_DD.RotateBackSurfaces();
// adjust rSrc back to just before start of the buffer rSrc.right = rSrc.right - m_bufferSize.cx;
rSrc.left = rSrc.left - m_bufferSize.cx; m_rSrcBlt = rSrc;
m_renderText = true; }
m_DD.Blt(m_rDstBlt, m_rSrcBlt); }
61
void CReaderView::scroller() {
if (!m_pause) {
// increment the scrolling source window position m_rSrcBlt.left += m_scrollIncrement;
m_rSrcBlt.right += m_scrollIncrement; }
// display the rendered text to screen displayText();
// perform a render to the offscreen buffer if necessary if ( m_renderText == true )
{
int charsRendered = 0;
// do the render, keeping track of characters shown charsRendered = renderText( m_textToShow );
// store an index to the last character displayed m_lastChar = m_lastChar + charsRendered;
// remove the remaining string yet to be rendered m_textToShow = m_textStrAll.Mid(m_lastChar); // check if a new string needs to be loaded if ( m_textToShow.IsEmpty() )
initiateText(); }
62
int CReaderView::renderText(CString drawMe) {
// obtain the desired font CFont font;
font.CreateFontIndirect(&m_logFont); // obtain the text to be displayed CString text = drawMe;
//// not safe for debugging beyond here – screen locked! // retrieve a handle to the GDI-style device context // of a DirectDraw offscreen (back) surface
HDC hDC;
m_DD.GetBackSurfaceDC( &hDC);
// fill the offscreen rectangle in the background color CBrush brBackground( m_backColour );
CRect rscreen = CRect(CPoint(0,0), m_surfaceSize ); FillRect(hDC, rscreen, brBackground);
// create a rectangle in the background color
CRect rect( 0, 0, m_bufferSize.cx, m_bufferSize.cy ); CBrush brBackground( m_backColour );
// prepare text attributes SetBkColor(hDC, m_backColour); SetTextColor(hDC, m_textColour); SelectObject( hDC, font );
// get widths of all characters in selected font GetCharWidth( hDC, 0, 255, &*m_lpAllCharWidths );
// calculate how much of string will fit on this surface for (int i = 0, lineLength = 0; i < text.GetLength(); i++) {
int charWidth = m_lpAllCharWidths[ text[ i ] ];
if ( lineLength + charWidth > m_surfaceSize.cx ) break; lineLength += charWidth;
}
// write the first i characters to the offscreen surface TextOut( hDC, 0, 0, text, i);
//// Now release the DC... screen unlocked again! m_DD.ReleaseBackSurfaceDC(hDC);
// now that the first i characters of text have been written // remove them from the string
text = text.Mid(i); m_bRenderText = false;
// return index of the last character displayed in given string return (drawMe.GetLength() - text.GetLength());
63
DDGraphics
void DDGraphics::Initialise( HWND hwndScreen ) {
// release any previous instances of DD object m_lpDD->Release();
m_lpDD = NULL;
CreateDD( hwndScreen ); GetCaps( hwndScreen );
// create surfaces with size of current screen CreatePrimarySurface( hwndScreen );
CreateBackSurfaces(); }
// create the DirectDraw object
// Note that extra code is required to ensure
// DirectDraw uses its version 2 interface, DirectDraw2 void DDGraphics::CreateDD( HWND hWnd )
{
LPDIRECTDRAW lpOldDD;
// create ‘old’ style DirectDraw object DirectDrawCreate( NULL, &lpOldDD, NULL ); // set DirectDraw for normal windows app
lpOldDD->SetCooperativeLevel( hWnd, DDSCL_NORMAL ); // query the interface for DirectDraw2 object
lpOldDD->QueryInterface( IID_IDirectDraw2, (LPVOID *)&m_lpDD ); // release old DD object
lpOldDD->Release(); }
64
void DDGraphics::GetCaps( HWND hWnd ) {
// get device capabilies
DDCAPS hwCaps = {0}, helCaps = {0}; hwCaps.dwSize = sizeof(DDCAPS);
helCaps.dwSize = sizeof(DDCAPS);
// look for hardware features of interest to us m_lpDD->GetCaps(&hwCaps, &helCaps);
// is there a h/w blitter to blt between surfaces
m_bCanBltVidMem = (hwCaps.dwCaps & DDCAPS_BLT) ? true : false; // find the maximum device screen width and height
HDC hdc;
hdc = GetDC(hWnd);
m_screenWidth = GetDeviceCaps( hdc, HORZRES ); m_screenHeight = GetDeviceCaps( hdc, VERTRES ); ReleaseDC(hWnd, hdc);
// find out how much video memory there is to play with DWORD dwTotal, dwFree;
DDSCAPS ddsCaps;
ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
m_lpDD->GetAvailableVidMem(&ddsCaps, &dwTotal, &dwFree); // check there is enough video memory for 4 surfaces // i.e. primary and 3 offscreen
if ( int(dwFree) < 4 * dwWidth * dwHeight ) m_bVidMemFree = false;
65
void DDGraphics::CreatePrimarySurface( HWND hWnd ) {
// create the primary surface and attach a window clipper // Note that to be compatible with DirectDraw2, the surface // must be set up using a DirectDrawSurface3 interface LPDIRECTDRAWSURFACE lpOldSurf;
LPDIRECTDRAWCLIPPER lpddClipper;
HDC hdc;
int i;
// initialise surface descriptor
ZeroMemory(&m_sdPrimary, sizeof(m_sdPrimary));
m_sdPrimary.dwSize = sizeof(m_sdPrimary);
m_sdPrimary.dwFlags = DDSD_CAPS;
m_sdPrimary.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
// create a primary surface
m_lpDD->CreateSurface(&m_sdPrimary, &lpOldSurf, NULL); // update to a DirectDraw3 compatible surface
lpOldSurf->QueryInterface(IID_IDirectDrawSurface3, &m_lpPrimSurf); // release the old surface pointer
lpOldSurf->Release();
// create a clipper: this ensures the primary surface is the // same size as the application’s window.
DirectDrawCreateClipper(0UL, &lpddClipper, NULL); // set the application window to be clipped
lpddClipper->SetHWnd(0UL, hWnd);
// attach the clipper to the primary surface and release pointer m_lpPrimSurf->SetClipper(lpddClipper);
lpddClipper->Release(); }
66
void DDGraphics::CreateBackSurfaces( HWND hWnd ) {
// Note that to be compatible with DirectDraw2, the surfaces // must be set up using a DirectDrawSurface3 interface
LPDIRECTDRAWSURFACE lpOldBackSurf1; LPDIRECTDRAWSURFACE lpOldBackSurf2; LPDIRECTDRAWSURFACE lpOldBackSurf; // set description of the back surfaces
m_sdBack.dwHeight = m_screenHeight;
m_sdBack.dwWidth = m_screenWidth;
m_sdBack.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
m_sdBack.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; // if we can use video memory, then do so.
if ( m_bCanBltVidMem && m_bVidMemFree )
m_sdBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; // create the first back surface
m_lpDD->CreateSurface(&m_sdBack, &lpOldBackSurf, NULL); // employ the DirectDraw3 imterface for this surface lpOldBackSurf->QueryInterface(IID_IDirectDrawSurface3,
(LPVOID *)&m_lpBackSurf); // release the old surface pointer
lpOldBackSurf->Release();
// do the same for 2nd surfaces
m_lpDD->CreateSurface(&m_sdBack, &lpOldBackSurf2, NULL); lpOldBackSurf2->QueryInterface( IID_IDirectDrawSurface3, (LPVOID *)&m_lpBltSurf2); // release the old surface pointer
lpOldBackSurf2->Release(); // and finally the 3r surface
m_lpDD->CreateSurface(&m_sdBack, &lpOldBackSurf1, NULL); lpOldBackSurf1->QueryInterface( IID_IDirectDrawSurface3, (LPVOID *)&m_lpBltSurf1); lpOldBackSurf1->Release();
67
void DDGraphics::Blt(CRect rDst, CRect rSrc) {
// blit from two separate surfaces, treating them a single buffer // assumes source and destination are the same size
CRect rSrc1 = rSrc, rSrc2 = rSrc, rDst1 = rDst, rDst2 = rDst; // destination width
int dwidth = rDst.right - rDst.left;
// rSrc1 and rSrc2 should never go outwith 0 and m_surfaceWidth // if source has negative left edge, wrap rSrc1
// back around onto its surface if ( rSrc.left <= 0 )
rSrc1.left = rSrc.left + m_surfaceWidth; // if right edge scrolls past buffer boundary, // wrap rSrc2 around to beginning of its surface if ( rSrc.right >= ( 2 * m_screenWidth ) )
rSrc2.right = rSrc.right - m_surfaceWidth; // set fixed points of two sub rectangles
rSrc1.right = surfaceWidth; rSrc2.left = 0;
// get new widths of source rectangles int width1 = rSrc1.right – rSrc1.left; int width2 = rSrc2.right – rSrc2.left;
// adjust destination rectangles to match these widths rDst1.right = rDst1.left + width1;
rDst2.left = rDst2.right – width2; // perform the blitting operations
// provided the source rectangles are valid if (width1 > 0)
m_lpPrimSurf->Blt( &rDst1, m_lpBltSurf1, &rSrc1 ); if (width2 > 0)
m_lpPrimSurf->Blt( &rDst2, m_lpBltSurf2, &rSrc2 ); }
68
void DDGraphics::RotateBackSurfaces(BOOL forward) {
// this function flips the three back surfaces // in a circular fashion LPDIRECTDRAWSURFACE3 tmpSurf; if (forward) { tmpSurf = m_lpBackSurf; m_lpBackSurf = m_lpBltSurf2; m_lpBltSurf2 = m_lpBltSurf1; m_lpBltSurf1 = tmpSurf; } else // backwards { tmpSurf = m_lpBltSurf1; m_lpBltSurf1 = m_lpBltSurf2; m_lpBltSurf2 = m_lpBackSurf; m_lpBackSurf = tmpSurf; } }
69
DDGraphics Page Flipping
The code for Page flipping is included for completeness. It has not yet been integrated into the main application.
void DDGraphics::CreateFlipSurfaces() {
// create two back buffer flippable surfaces LPDIRECTDRAWSURFACE lpFlipSurf1;
LPDIRECTDRAWSURFACE lpFlipSurf2;
// flippable back surfaces should be almost identical to primary // so describe primary surface and copy onto flip descriptionS m_lpPrimSurf->GetSurfaceDesc(&m_sdPrimary);
memcpy(&m_sdFlip, &m_sdPrimary, sizeof(DDSURFACEDESC)); m_sdFlip.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
m_sdFlip.dwFlags = DDSD_WIDTH | DDSD_HEIGHT
| DDSD_PIXELFORMAT | DDSD_CAPS; // if we can use video memory, then do so.
if (m_bCanBltVidMem)
m_sdBack.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY; // now create flip surface 1 in similar manner as before m_lpDD->CreateSurface(&m_sdFlip, &lpFlipSurf1, NULL); lpFlipSurf1->QueryInterface( IID_IDirectDrawSurface3,
(LPVOID *)&m_lpFlipSurf1); // now create flip surface 2
m_lpDD->CreateSurface(&m_sdFlip, &lpFlipSurf2, NULL); lpFlipSurf1->QueryInterface( IID_IDirectDrawSurface3,
(LPVOID *)&m_lpFlipSurf2); // attach flip surfaces to the primary
m_lpPrimSurf->AddAttachedSurface(m_lpFlipSurf1); m_lpPrimSurf->AddAttachedSurface(m_lpFlipSurf2); }
void DDGraphics::Flip() {
// flip with wait until finished flag
return m_lpPrimSurf->Flip(NULL, (DWORD)DDFLIP_WAIT); }
70