/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_canvas.hxx"

#if DIRECTX_VERSION < 0x0900

// Nvidia GeForce Go 6800 crashes with a bluescreen if we take the
// maximum texture size, which would be twice as large. this behaviors
// has only been observed on directx5.
// This value is simply the maximum size for textures we request from
// the system, it has absolutely nothing to do with the size of primitives
// we're able to render, both concepts are totally independent from each other.
#define MAX_TEXTURE_SIZE (2048)
#define MIN_TEXTURE_SIZE (32)
//#define FAKE_MAX_NUMBER_TEXTURES (2)
//#define FAKE_MAX_TEXTURE_SIZE (512)

//////////////////////////////////////////////////////////////////////////////////
// includes
//////////////////////////////////////////////////////////////////////////////////
#include <vcl/syschild.hxx>
#include <vcl/window.hxx>
#include <canvas/debug.hxx>
#include <canvas/verbosetrace.hxx>
#include <canvas/elapsedtime.hxx>
#include <canvas/canvastools.hxx>
#include <canvas/rendering/icolorbuffer.hxx>
#include <canvas/rendering/isurface.hxx>
#include <canvas/rendering/irendermodule.hxx>
#include <tools/diagnose_ex.h>
#include <basegfx/numeric/ftools.hxx>
#include <basegfx/vector/b2dsize.hxx>
#include <basegfx/vector/b2isize.hxx>
#include <basegfx/point/b2ipoint.hxx>
#include <basegfx/range/b2irectangle.hxx>
#include <boost/scoped_ptr.hpp>
#include <com/sun/star/lang/NoSupportException.hpp>

#define COMPILE_MULTIMON_STUBS

#include "dx_rendermodule.hxx"
#include "dx_surfacegraphics.hxx"
#include <vcl/sysdata.hxx>

#undef WB_LEFT
#undef WB_RIGHT

#include "dx_impltools.hxx"
#include <malloc.h>

#if defined(DX_DEBUG_IMAGES) 
# if OSL_DEBUG_LEVEL > 0
#  include <imdebug.h>
#  undef min
#  undef max
# endif
#endif

#undef COMPILE_MULTIMON_STUBS

#include <stdio.h>

#define MONITOR_DEFAULTTONULL       0x00000000
#define MONITOR_DEFAULTTOPRIMARY    0x00000001
#define MONITOR_DEFAULTTONEAREST    0x00000002

using namespace ::com::sun::star;

//////////////////////////////////////////////////////////////////////////////////
// 'dxcanvas' namespace
//////////////////////////////////////////////////////////////////////////////////

namespace dxcanvas
{
    namespace
    {
        bool doBlit( const ::basegfx::B2IPoint& rDestPos,
                     IDirectDrawSurface&        rOutSurface,
                     const ::basegfx::B2IRange& rSourceArea,
                     IDirectDrawSurface&        rSourceSurface,
                     DDBLTFX*                   pBltFx,
                     bool                       bForceSoftware )
        {
            if( !bForceSoftware )
            {
                // blit surface to backbuffer
                RECT aOutRect =
                    {
                        rDestPos.getX(),
                        rDestPos.getY(),
                        rDestPos.getX() + static_cast<sal_Int32>(rSourceArea.getWidth()),
                        rDestPos.getY() + static_cast<sal_Int32>(rSourceArea.getHeight()),
                    };
                RECT aSourceRect =
                    {
                        rSourceArea.getMinX(),
                        rSourceArea.getMinY(),
                        rSourceArea.getMaxX(),
                        rSourceArea.getMaxY()
                    };

                if( SUCCEEDED(rOutSurface.Blt( &aOutRect,
                                               &rSourceSurface,
                                               &aSourceRect,
                                               DDBLT_WAIT,
                                               pBltFx )) )
                {
                    return true;
                }
            }

            // failed, or forced to use SW copy. attempt manual copy.
            bool bResult = false;

            // lock source surface
            DDSURFACEDESC aDescSrc;
            rtl_fillMemory(&aDescSrc,sizeof(DDSURFACEDESC),0);
            aDescSrc.dwSize = sizeof(DDSURFACEDESC);
            const DWORD dwSrcFlags = DDLOCK_NOSYSLOCK|
                DDLOCK_SURFACEMEMORYPTR|
                DDLOCK_WAIT|
                DDLOCK_READONLY;
            if(SUCCEEDED(rSourceSurface.Lock(NULL,
                                             &aDescSrc,
                                             dwSrcFlags,
                                             NULL)))
            {
                // lock destination surface
                DDSURFACEDESC aDescDst;
                rtl_fillMemory(&aDescDst,sizeof(DDSURFACEDESC),0);
                aDescDst.dwSize = sizeof(DDSURFACEDESC);
                const DWORD dwDstFlags = DDLOCK_NOSYSLOCK|
                    DDLOCK_SURFACEMEMORYPTR|
                    DDLOCK_WAIT|
                    DDLOCK_WRITEONLY;
                if(SUCCEEDED(rOutSurface.Lock(NULL,
                                              &aDescDst,
                                              dwDstFlags,
                                              NULL)))
                {
                    sal_uInt32 nSrcFormat;
                    nSrcFormat  = ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
                    nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwRBitMask)<<8;
                    nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwGBitMask)<<4;
                    nSrcFormat |= ::canvas::tools::bitcount32(aDescSrc.ddpfPixelFormat.dwBBitMask);

                    sal_uInt32 nDstFormat;
                    nDstFormat  = ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
                    nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwRBitMask)<<8;
                    nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwGBitMask)<<4;
                    nDstFormat |= ::canvas::tools::bitcount32(aDescDst.ddpfPixelFormat.dwBBitMask);

                    // TODO(E1): Use numeric_cast to catch overflow here
                    const sal_uInt32 nWidth( static_cast<sal_uInt32>(
                                                   rSourceArea.getWidth() ) );
                    const sal_uInt32 nHeight( static_cast<sal_uInt32>(
                                                    rSourceArea.getHeight() ) );

                    if((nSrcFormat == 0x8888) && (nDstFormat == 0x0565))
                    {
                        // medium range 8888 to 0565 pixel format conversion.
                        bResult = true;
                        sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
                            rSourceArea.getMinY()*aDescSrc.lPitch +
                            (rSourceArea.getMinX()<<2);
                        sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
                            rDestPos.getY()*aDescDst.lPitch +
                            (rDestPos.getX()<<1);
                        for(sal_uInt32 y=0; y<nHeight; ++y)
                        {
                            sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface;
                            sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
                            for(sal_uInt32 x=0; x<nWidth; ++x)
                            {
                                sal_uInt32 srcPixel = *pSrcScanline++;
                                sal_uInt16 dstPixel;
                                dstPixel  = (sal_uInt16)((srcPixel & 0x0000F8) >> 3);
                                dstPixel |= (srcPixel & 0x00FC00) >> 5;
                                dstPixel |= (srcPixel & 0xF80000) >> 8;
                                *pDstScanline++ = dstPixel;
                            }
                            pSrcSurface += aDescSrc.lPitch;
                            pDstSurface += aDescDst.lPitch;
                        }
                    }
                    else if((nSrcFormat == 0x8888) && (nDstFormat == 0x0888))
                    {
                        // medium range 8888 to 0888 pixel format conversion.
                        bResult = true;
                        sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
                            rSourceArea.getMinY()*aDescSrc.lPitch +
                            (rSourceArea.getMinX()<<2);
                        sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
                            rDestPos.getY()*aDescDst.lPitch +
                            (rDestPos.getX()<<2);
                        for(sal_uInt32 y=0; y<nHeight; ++y)
                        {
                            sal_uInt32 *pSrcScanline = (sal_uInt32 *)pSrcSurface;
                            sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
                            for(sal_uInt32 x=0; x<nWidth; ++x)
                            {
                                *pDstScanline++ = (sal_uInt16)*pSrcScanline++;
                            }
                            pSrcSurface += aDescSrc.lPitch;
                            pDstSurface += aDescDst.lPitch;
                        }
                    }
                    else if((nSrcFormat == 0x8888) && (nDstFormat == 0x1555))
                    {
                        // medium range 8888 to 1555 pixel format conversion.
                        bResult = true;
                        sal_uInt8 *pSrcSurface = (sal_uInt8 *)aDescSrc.lpSurface +
                            rSourceArea.getMinY()*aDescSrc.lPitch +
                            (rSourceArea.getMinX()<<2);
                        sal_uInt8 *pDstSurface = (sal_uInt8 *)aDescDst.lpSurface +
                            rDestPos.getY()*aDescDst.lPitch +
                            (rDestPos.getX()<<1);
                        for(sal_uInt32 y=0; y<nHeight; ++y)
                        {
                            sal_uInt32 *pSrcScanline = (sal_uInt32*)pSrcSurface;
                            sal_uInt16 *pDstScanline = (sal_uInt16 *)pDstSurface;
                            for(sal_uInt32 x=0; x<nWidth; ++x)
                            {
                                sal_uInt32 srcPixel = *pSrcScanline++;
                                sal_uInt16 dstPixel;
                                dstPixel  = (sal_uInt16)((srcPixel & 0x000000F8) >> 3);
                                dstPixel |= (srcPixel & 0x0000F800) >> 6;
                                dstPixel |= (srcPixel & 0x00F80000) >> 9;
                                dstPixel |= (srcPixel & 0x80000000) >> 16;
                                *pDstScanline++ = dstPixel;
                            }
                            pSrcSurface += aDescSrc.lPitch;
                            pDstSurface += aDescDst.lPitch;
                        }
                    }

                    // unlock destination surface
                    rOutSurface.Unlock(NULL);
                }

                // unlock source surface
                rSourceSurface.Unlock(NULL);
            }

            return bResult;
        }

        void dumpSurface( const COMReference<IDirectDrawSurface> &pSurface, const char *szFilename )
        {
            if(!(pSurface.get()))
                return;

            DDSURFACEDESC aSurfaceDesc;
            rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
            aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);

            if( FAILED(pSurface->Lock( NULL,
                                        &aSurfaceDesc,
                                        DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY,
                                        NULL)) )
                return;

            const std::size_t dwBitmapSize(aSurfaceDesc.dwWidth*aSurfaceDesc.dwHeight*4);
            sal_uInt8 *pBuffer = static_cast<sal_uInt8 *>(_alloca(dwBitmapSize));
            if(pBuffer)
            {
                sal_uInt8 *pSource = reinterpret_cast<sal_uInt8 *>(aSurfaceDesc.lpSurface);
                sal_uInt8 *pDest = reinterpret_cast<sal_uInt8 *>(pBuffer);
                const std::size_t dwDestPitch(aSurfaceDesc.dwWidth<<2);
                pDest += aSurfaceDesc.dwHeight*dwDestPitch;
                for(sal_uInt32 y=0; y<aSurfaceDesc.dwHeight; ++y)
                {
                    pDest -= dwDestPitch;
                    rtl_copyMemory( pDest, pSource, dwDestPitch );
                    pSource += aSurfaceDesc.lPitch;
                }

                if(FILE *fp = fopen(szFilename,"wb"))
                {
                    BITMAPINFOHEADER bitmapInfo;

                    bitmapInfo.biSize = sizeof(BITMAPINFOHEADER);
                    bitmapInfo.biWidth = aSurfaceDesc.dwWidth;
                    bitmapInfo.biHeight = aSurfaceDesc.dwHeight;
                    bitmapInfo.biPlanes = 1;
                    bitmapInfo.biBitCount = 32;
                    bitmapInfo.biCompression = BI_RGB;
                    bitmapInfo.biSizeImage = 0;
                    bitmapInfo.biXPelsPerMeter = 0;
                    bitmapInfo.biYPelsPerMeter = 0;
                    bitmapInfo.biClrUsed = 0;
                    bitmapInfo.biClrImportant = 0;

                    const std::size_t dwFileSize(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwBitmapSize);

                    BITMAPFILEHEADER header;
                    header.bfType = 'MB';
                    header.bfSize = dwFileSize;
                    header.bfReserved1 = 0;
                    header.bfReserved2 = 0;
                    header.bfOffBits = sizeof(BITMAPFILEHEADER) + bitmapInfo.biSize;

                    fwrite(&header,1,sizeof(BITMAPFILEHEADER),fp);
                    fwrite(&bitmapInfo,1,sizeof(BITMAPINFOHEADER),fp);
                    fwrite(pBuffer,1,dwBitmapSize,fp);

                    fclose(fp);
                }
            }

            pSurface->Unlock(NULL);
        }

        void clearSurface( const COMReference<IDirectDrawSurface>& pSurface )
        {
            if(!(pSurface.is()))
                return;

            DDBLTFX aBltFx;

            rtl_fillMemory( &aBltFx,
                            sizeof(DDBLTFX), 0 );
            aBltFx.dwSize = sizeof(DDBLTFX);
            aBltFx.dwFillColor = 0;

            pSurface->Blt( NULL,
                           NULL,
                           NULL,
                           DDBLT_COLORFILL | DDBLT_WAIT,
                           &aBltFx );
        }

        // Define struct for MonitorEntry
        struct MonitorEntry
        {
             GUID                 mnGUID;
             HMONITOR             mhMonitor;
            MONITORINFO   maMonitorInfo;
        };

        // define type for MonitorList
        typedef ::std::vector< MonitorEntry > MonitorList;

         // Win32 system callback for DirectDrawEnumerateExA call
         BOOL WINAPI EnumerateExA_Callback( GUID FAR* lpGUID,
                                           LPSTR     /*lpDriverDescription*/,
                                           LPSTR     /*lpDriverName*/,
                                           LPVOID    lpContext,
                                           HMONITOR  hMonitor )
        {
            if(lpGUID)
            {
                 MonitorList* pMonitorList = (MonitorList*)lpContext;
                MonitorEntry aEntry;

                 aEntry.mnGUID = *lpGUID;
                 aEntry.mhMonitor = hMonitor;
                aEntry.maMonitorInfo.cbSize = sizeof(MONITORINFO);
                GetMonitorInfo( hMonitor,
                                &aEntry.maMonitorInfo );

                pMonitorList->push_back(aEntry);
            }

            return DDENUMRET_OK;
        }

        void fillMonitorList( MonitorList& rMonitorList )
        {
            // Try to fill MonitorList. If neither lib or call to
            // DirectDrawEnumerateExA does not exist, it's an old
            // DX version (< 5.0), or system does not support
            // multiple monitors.
            HINSTANCE hInstance = LoadLibrary("ddraw.dll");

            if(hInstance)
            {
                LPDIRECTDRAWENUMERATEEX lpDDEnumEx =
                    (LPDIRECTDRAWENUMERATEEX)GetProcAddress(hInstance,"DirectDrawEnumerateExA");

                if(lpDDEnumEx)
                    lpDDEnumEx( (LPDDENUMCALLBACKEXA) EnumerateExA_Callback,
                                &rMonitorList,
                                DDENUM_ATTACHEDSECONDARYDEVICES );

                FreeLibrary(hInstance);
            }
        }

        IDirectDraw2* createDirectDraw( const MonitorList& rMonitorList,
                                        MONITORINFO&       rMonitorInfo,
                                        HWND        renderWindow )
        {
             GUID* gpSelectedDriverGUID = NULL;

            // if we have multiple monitors, choose a gpSelectedDriverGUID from monitor list
            HMONITOR hMonitor = MonitorFromWindow(renderWindow,
                                                  MONITOR_DEFAULTTONEAREST);

            MonitorList::const_iterator       aCurr = rMonitorList.begin();
            const MonitorList::const_iterator aEnd = rMonitorList.end();
            while( !gpSelectedDriverGUID && aCurr != aEnd )
            {
                if(hMonitor == aCurr->mhMonitor)
                {
                    // This is the monitor we are running on
                    gpSelectedDriverGUID = const_cast<GUID*>(&aCurr->mnGUID);
                    rMonitorInfo = aCurr->maMonitorInfo;
                }

                ++aCurr;
            }

            IDirectDraw* pDirectDraw;
            if( FAILED( DirectDrawCreate( gpSelectedDriverGUID, &pDirectDraw, NULL )))
                return NULL;

            IDirectDraw2* pDirectDraw2;
            if( FAILED( pDirectDraw->QueryInterface( IID_IDirectDraw2, (LPVOID*)&pDirectDraw2 )))
                return NULL;

            // queryInterface bumped up the refcount, so release the
            // reference to the original IDirectDraw interface.
            pDirectDraw->Release();

            return pDirectDraw2;
        }

        HRESULT WINAPI EnumTextureFormatsCallback( LPDDSURFACEDESC 	pSurfaceDesc,
                                                   LPVOID			pContext		)
        {
            // dirty cast of given context back to result ModeSelectContext
            DDPIXELFORMAT* pResult = (DDPIXELFORMAT*)pContext;

            if( pResult == NULL || pSurfaceDesc == NULL )
                return DDENUMRET_CANCEL;

            VERBOSE_TRACE( "EnumTextureFormatsCallback: advertised texture format has dwRGBBitCount %d, dwRBitMask %x, "
                           "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The format uses %s alpha.",
                           pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
                           pSurfaceDesc->ddpfPixelFormat.dwRBitMask,
                           pSurfaceDesc->ddpfPixelFormat.dwGBitMask,
                           pSurfaceDesc->ddpfPixelFormat.dwBBitMask,
                           pSurfaceDesc->ddpfPixelFormat.dwRGBAlphaBitMask,
                           pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" );

            // Only accept RGB surfaces with alpha channel
            if( (DDPF_ALPHAPIXELS | DDPF_RGB) ==
                (pSurfaceDesc->ddpfPixelFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) )
            {
                // ignore formats with the DDPF_ALPHAPREMULT flag
                if(!(pSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_ALPHAPREMULT))
                {
                    // take widest alpha channel available
                    if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth > pResult->dwAlphaBitDepth )
                    {
                        // take new format
                        rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) );
                    }
                    else if( pSurfaceDesc->ddpfPixelFormat.dwAlphaBitDepth == pResult->dwAlphaBitDepth )
                    {
                        // tie-breaking: take highest bitcount
                        if( pSurfaceDesc->ddpfPixelFormat.dwRGBBitCount > pResult->dwRGBBitCount )
                        {
                            // take new format
                            rtl_copyMemory( pResult, &pSurfaceDesc->ddpfPixelFormat, sizeof(DDPIXELFORMAT) );
                        }
                    }
                }
            }

            return DDENUMRET_OK;
        }

        class DXRenderModule;

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface
        //////////////////////////////////////////////////////////////////////////////////

        /** ISurface implemenation.

            @attention holds the DXRenderModule via non-refcounted
            reference! This is safe with current state of affairs, since
            the canvas::PageManager holds surface and render module via
            shared_ptr (and makes sure all surfaces are deleted before its
            render module member goes out of scope).
        */
        class DXSurface : public canvas::ISurface
        {
        public:
            DXSurface( DXRenderModule&           rRenderModule,
                       const ::basegfx::B2ISize& rSize );
            ~DXSurface();

            virtual bool selectTexture();
            virtual bool isValid();
            virtual bool update( const ::basegfx::B2IPoint& rDestPos,
                                 const ::basegfx::B2IRange& rSourceRect,
                                 ::canvas::IColorBuffer&    rSource );
            virtual ::basegfx::B2IVector getSize();

        private:
            /// Guard local methods against concurrent acces to RenderModule
            class ImplRenderModuleGuard : private ::boost::noncopyable
            {
            public:
                explicit inline ImplRenderModuleGuard( DXRenderModule& rRenderModule );
                inline ~ImplRenderModuleGuard();

            private:
                DXRenderModule& mrRenderModule;
            };

            DXRenderModule&                         mrRenderModule;

            COMReference<IDirectDrawSurface> mpSurface;
            COMReference<IDirect3DTexture2>  mpTexture;

            ::basegfx::B2IVector maSize;
        };

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule
        //////////////////////////////////////////////////////////////////////////////////

        /// Default implementation of IDXRenderModule
        class DXRenderModule : public IDXRenderModule
        {
        public:
            explicit DXRenderModule( const ::Window& rWindow );

            virtual void lock() const { maMutex.acquire(); }
            virtual void unlock() const { maMutex.release(); }

            virtual COMReference<IDirectDrawSurface>
                createSystemMemorySurface( const ::basegfx::B2IVector& rSize );

            virtual bool flip( const ::basegfx::B2IRectangle& rUpdateArea,
                               const ::basegfx::B2IRectangle& rCurrWindowArea );

            virtual void resize( const ::basegfx::B2IRange& rect );
            virtual HWND getHWND() const { return mhWnd; }
            virtual void disposing();
            virtual void screenShot();
            virtual ::basegfx::B2IVector getPageSize();
            virtual ::canvas::ISurfaceSharedPtr createSurface( const ::basegfx::B2IVector& surfaceSize );
            virtual void beginPrimitive( PrimitiveType eType );
            virtual void endPrimitive();
            virtual void pushVertex( const ::canvas::Vertex& vertex );
            virtual bool isError();

            const D3DDEVICEDESC&             getDeviceDesc() const { return maDeviceDesc; }
            const DDPIXELFORMAT&             getTextureFormat() const { return maTextureFormat; }
            COMReference<IDirectDraw2>       getDirectDraw() { return mpDirectDraw; }
            COMReference< IDirect3DDevice2 > getDevice() { return mpDirect3DDevice; }

            void flushVertexCache();

            struct ModeSelectContext
            {
                DDSURFACEDESC selectedDesc;
                ::basegfx::B2ISize	 requestedSize;
            };

            /** Query actual size of the device

                This is especially interesting for fullscreen devices
            */
            ::basegfx::B2ISize getFramebufferSize() const;

            /** Query the amount of memory available for new surfaces

                This might differ from getAvailableTextureMem()
                @see getAvailableTextureMem()

                @param bWithAGPMema
                When true, returned value includes non-local,
                i.e. AGP-able memory, too.

                @return the amount of free surface mem
            */
            std::size_t	  getAvailableSurfaceMem( bool bWithAGPMem=true ) const;

            /** Query the amount of memory available for new textures

                This might differ from getAvailableSurfaceMem()
                @see getAvailableSurfaceMem()

                @param bWithAGPMema
                When true, returned value includes non-local,
                i.e. AGP-able memory, too.

                @return the amount of free texture mem
            */
            std::size_t     getAvailableTextureMem( bool bWithAGPMem=true ) const;

        private:
            bool queryCaps();
            bool validateCaps();
            bool setup3DDevice();
            unsigned int getDisplayFormat() const;

            void convert2Screen( ::basegfx::B2IPoint& io_rDestPos,
                                 ::basegfx::B2IRange& io_rDestArea );

            void renderInfoText( const ::rtl::OUString& rStr,
                                 const Gdiplus::PointF& rPos ) const;
            void renderFPSCounter() const;
            void renderMemAvailable() const;

            bool create( const ::Window& rWindow );
            bool validateMainSurfaces();

            /** This object represents the DirectX state machine.  In order
                to serialize access to DirectX's global state, a global
                mutex is required.
            */
            static ::osl::Mutex						maMutex;

            HWND                                    mhWnd;
            ::boost::scoped_ptr<SystemChildWindow>	mpWindow;
            ::basegfx::B2IVector					maSize;
                
            ModeSelectContext						maSelectedFullscreenMode;
            DDPIXELFORMAT                           maTextureFormat;
        
            MONITORINFO                             maMonitorInfo; // monitor info for mpDirectDraw's monitor
            COMReference<IDirectDraw2>              mpDirectDraw;
            COMReference<IDirectDrawSurface>        mpPrimarySurface;
            COMReference<IDirectDrawSurface>        mpBackBufferSurface;

            COMReference< IDirect3D2 >              mpDirect3D;
            COMReference< IDirect3DDevice2 >        mpDirect3DDevice;

            mutable ::canvas::tools::ElapsedTime	maLastUpdate;	// for the frame counter

            D3DDEVICEDESC                           maDeviceDesc;

            typedef std::vector<canvas::Vertex>	    vertexCache_t;
            vertexCache_t							maVertexCache;
            std::size_t								mnCount;

            int                                     mnBeginSceneCount;
                
            const bool								mbPageFlipping;
            bool 									mbHasNoTearingBlt;
            bool									mbError;
            PrimitiveType							meType;

            ::canvas::ISurfaceSharedPtr				mpTexture;
            ::basegfx::B2IVector					maPageSize;
        };

        ::osl::Mutex DXRenderModule::maMutex;

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::ImplRenderModuleGuard
        //////////////////////////////////////////////////////////////////////////////////

        inline DXSurface::ImplRenderModuleGuard::ImplRenderModuleGuard( 
            DXRenderModule& rRenderModule ) :
            mrRenderModule( rRenderModule )
        {
            mrRenderModule.lock();
        }

        inline DXSurface::ImplRenderModuleGuard::~ImplRenderModuleGuard()
        {
            mrRenderModule.unlock();
        }

#ifdef FAKE_MAX_NUMBER_TEXTURES
        static sal_uInt32 gNumSurfaces = 0;
#endif

        void fillRect( sal_uInt32 *pDest,
                       sal_uInt32 dwWidth,
                       sal_uInt32 dwHeight,
                       sal_uInt32 dwPitch,
                       sal_uInt32 dwColor )
        {
            for(sal_uInt32 i=0; i<dwWidth; ++i)
            {
                pDest[i]=dwColor;
                pDest[((dwHeight-1)*dwPitch)+i]=dwColor;
            }

            for(sal_uInt32 j=0; j<dwHeight; ++j)
            {
                pDest[0]=dwColor;
                pDest[dwWidth-1]=dwColor;
                pDest += dwPitch;
            }
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::DXSurface
        //////////////////////////////////////////////////////////////////////////////////

        DXSurface::DXSurface( DXRenderModule&           rRenderModule,
                              const ::basegfx::B2ISize& rSize ) : 
            mrRenderModule(rRenderModule),
            mpTexture(NULL),
            mpSurface(NULL),
            maSize()
        {
            ImplRenderModuleGuard aGuard( mrRenderModule );

#ifdef FAKE_MAX_NUMBER_TEXTURES
            ++gNumSurfaces;
            if(gNumSurfaces >= FAKE_MAX_NUMBER_TEXTURES)
                return;
#endif

#ifdef FAKE_MAX_TEXTURE_SIZE
            if(rSize.getX() > FAKE_MAX_TEXTURE_SIZE)
                return;
            if(rSize.getY() > FAKE_MAX_TEXTURE_SIZE)
                return;
#endif

            ENSURE_ARG_OR_THROW(rSize.getX() > 0 && rSize.getY() > 0, 
                            "DXSurface::DXSurface(): request for zero-sized surface");

            const D3DDEVICEDESC &deviceDesc = rRenderModule.getDeviceDesc();

            DDSURFACEDESC aSurfaceDesc;
            rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
            aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
            aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
            aSurfaceDesc.dwWidth = ::std::min(deviceDesc.dwMaxTextureWidth,::canvas::tools::nextPow2(rSize.getX()));
            aSurfaceDesc.dwHeight = ::std::min(deviceDesc.dwMaxTextureHeight,::canvas::tools::nextPow2(rSize.getY()));
            aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE |
                                          DDSCAPS_VIDEOMEMORY |
                                          DDSCAPS_LOCALVIDMEM;
            rtl_copyMemory(&aSurfaceDesc.ddpfPixelFormat,&rRenderModule.getTextureFormat(),sizeof(DDPIXELFORMAT));

            IDirectDrawSurface *pSurface;
            COMReference<IDirectDraw2> pDirectDraw(rRenderModule.getDirectDraw());
            HRESULT hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
            if(FAILED(hr))
            {
                // if the call failed due to 'out of videomemory',
                // retry with request for AGP memory.
                if(DDERR_OUTOFVIDEOMEMORY == hr)
                {
                    aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_TEXTURE |
                                                  DDSCAPS_VIDEOMEMORY |
                                                  DDSCAPS_NONLOCALVIDMEM;
                    hr = pDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
                }
            }

            if(SUCCEEDED(hr))
            {
                IDirect3DTexture2* pTexture;
                if( FAILED(pSurface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&pTexture)) )
                {
                    pSurface->Release();
                    return;
                }

                maSize.setX(aSurfaceDesc.dwWidth);
                maSize.setY(aSurfaceDesc.dwHeight);

                mpSurface=COMReference<IDirectDrawSurface>(pSurface);
                mpTexture=COMReference<IDirect3DTexture2>(pTexture);

                // #122683# Clear texture, to avoid ugly artifacts at the
                // border to invisible sprite areas (note that the textures
                // are usually only partly utilized).
                clearSurface( mpSurface );
            }
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::~DXSurface
        //////////////////////////////////////////////////////////////////////////////////

        DXSurface::~DXSurface()
        {
            ImplRenderModuleGuard aGuard( mrRenderModule );

#ifdef FAKE_MAX_NUMBER_TEXTURES
            gNumSurfaces--;
#endif
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::selectTexture
        //////////////////////////////////////////////////////////////////////////////////

        bool DXSurface::selectTexture()
        {
            ImplRenderModuleGuard aGuard( mrRenderModule );

            mrRenderModule.flushVertexCache();

            D3DTEXTUREHANDLE aTextureHandle;
            if(FAILED(mpTexture->GetHandle(
                          mrRenderModule.getDevice().get(),
                          &aTextureHandle)))
            {
                return false;
            }

            // select texture for next primitive
            if(FAILED(mrRenderModule.getDevice()->SetRenderState(
                          D3DRENDERSTATE_TEXTUREHANDLE,aTextureHandle)))
            {
                return false;
            }

#if defined(DX_DEBUG_IMAGES) 
# if OSL_DEBUG_LEVEL > 0
            if( mpSurface.is() )
            {
                DDSURFACEDESC aSurfaceDesc;
                rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
                aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);

                if( SUCCEEDED(mpSurface->Lock( NULL, 
                                               &aSurfaceDesc, 
                                               DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_READONLY,
                                               NULL)) )
                {
                    imdebug( "rgba w=%d h=%d %p", 
                             aSurfaceDesc.dwWidth, 
                             aSurfaceDesc.dwHeight, 
                             aSurfaceDesc.lpSurface );

                    mpSurface->Unlock(NULL);
                }
            }
# endif
#endif

            return true;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::isValid
        //////////////////////////////////////////////////////////////////////////////////

        bool DXSurface::isValid()
        {
            ImplRenderModuleGuard aGuard( mrRenderModule );

            if(!(mpSurface.is()))
                return false;

            if(mpSurface->IsLost() == DDERR_SURFACELOST) 
            {
                mpSurface->Restore();
                return false;
            }

            return true;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::update
        //////////////////////////////////////////////////////////////////////////////////

        bool DXSurface::update( const ::basegfx::B2IPoint& rDestPos,
                                const ::basegfx::B2IRange& rSourceRect,
                                ::canvas::IColorBuffer&    rSource )
        {
            ImplRenderModuleGuard aGuard( mrRenderModule );

            // can't update if surface is not valid, that means
            // either not existent nor restored...
            if(!(isValid()))
                return false;

            DDSURFACEDESC aSurfaceDesc;
            rtl_fillMemory( &aSurfaceDesc,sizeof(DDSURFACEDESC),0 );
            aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);

            // TODO(P2): only lock the region we want to update
            if( FAILED(mpSurface->Lock( NULL, 
                                        &aSurfaceDesc, 
                                        DDLOCK_NOSYSLOCK|DDLOCK_SURFACEMEMORYPTR|DDLOCK_WAIT|DDLOCK_WRITEONLY,
                                        NULL)) )
                return false;

            if(sal_uInt8* pImage = rSource.lock())
            {
                switch( rSource.getFormat() )
                {
                    case ::canvas::IColorBuffer::FMT_A8R8G8B8:
                    {
                        const std::size_t nSourceBytesPerPixel(4);
                        const std::size_t nSourcePitchInBytes(rSource.getStride());
                        pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
                        pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;

                        // calculate the destination memory address
                        sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+
                                           (rDestPos.getY()*aSurfaceDesc.lPitch) + 
                                           (4*rDestPos.getX()));

                        const sal_uInt32 nNumBytesToCopy(
                            static_cast<sal_uInt32>(
                                rSourceRect.getWidth())*
                            nSourceBytesPerPixel);
                        const sal_uInt64 nNumLines(rSourceRect.getHeight());

                        for(sal_uInt32 i=0; i<nNumLines; ++i)
                        {
                            rtl_copyMemory(pDst,pImage,nNumBytesToCopy);

                            pDst += aSurfaceDesc.lPitch;
                            pImage += nSourcePitchInBytes;
                        }
                    }
                    break;

                    case ::canvas::IColorBuffer::FMT_R8G8B8:
                    {
                        const std::size_t nSourceBytesPerPixel(3);
                        const std::size_t nSourcePitchInBytes(rSource.getStride());
                        pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
                        pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;

                        // calculate the destination memory address
                        sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+
                                           (rDestPos.getY()*aSurfaceDesc.lPitch) + 
                                           (4*rDestPos.getX()));

                        const sal_uInt64 nNumColumns(rSourceRect.getWidth());
                        const sal_uInt64 nNumLines(rSourceRect.getHeight());
                        for(sal_uInt32 i=0; i<nNumLines; ++i)
                        {
                            sal_uInt32 *pDstScanline = reinterpret_cast<sal_uInt32 *>(pDst);
                            sal_uInt8 *pSrcScanline = reinterpret_cast<sal_uInt8 *>(pImage);
                            for(sal_uInt32 x=0; x<nNumColumns; ++x)
                            {
                                sal_uInt32 color(0xFF000000);
                                color |= pSrcScanline[2]<<16;
                                color |= pSrcScanline[1]<<8;
                                color |= pSrcScanline[0];
                                pSrcScanline += 3;
                                *pDstScanline++ = color;
                            }

                            pDst += aSurfaceDesc.lPitch;
                            pImage += nSourcePitchInBytes;
                        }
                    }
                    break;

                    case ::canvas::IColorBuffer::FMT_X8R8G8B8:
                    {
                        const std::size_t nSourceBytesPerPixel(4);
                        const std::size_t nSourcePitchInBytes(rSource.getStride());
                        pImage += rSourceRect.getMinY()*nSourcePitchInBytes;
                        pImage += rSourceRect.getMinX()*nSourceBytesPerPixel;

                        // calculate the destination memory address
                        sal_uInt8 *pDst = ((sal_uInt8*)aSurfaceDesc.lpSurface+
                                           (rDestPos.getY()*aSurfaceDesc.lPitch) + 
                                           (4*rDestPos.getX()));

                        const sal_uInt64 nNumLines(rSourceRect.getHeight());

                        for(sal_uInt32 i=0; i<nNumLines; ++i)
                        {
                            sal_uInt32 *pSrc32 = reinterpret_cast<sal_uInt32 *>(pImage);
                            sal_uInt32 *pDst32 = reinterpret_cast<sal_uInt32 *>(pDst);
                            for(sal_uInt32 j=0; j<rSourceRect.getWidth(); ++j)
                                pDst32[j] = 0xFF000000 | pSrc32[j];

                            pDst += aSurfaceDesc.lPitch;
                            pImage += nSourcePitchInBytes;
                        }
                    }
                    break;

                    default:
                        ENSURE_OR_RETURN_FALSE(false, 
                                          "DXSurface::update(): Unknown/unimplemented buffer format" );
                        break;
                }

                rSource.unlock();
            }

            return SUCCEEDED(mpSurface->Unlock(NULL));
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXSurface::getSize
        //////////////////////////////////////////////////////////////////////////////////
        
        ::basegfx::B2IVector DXSurface::getSize()
        {
            return maSize;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::DXRenderModule
        //////////////////////////////////////////////////////////////////////////////////

        DXRenderModule::DXRenderModule( const ::Window& rWindow ) :
            mhWnd(0),
            mpWindow(),
            maSize(),
            maSelectedFullscreenMode(),
            maTextureFormat(),
            maMonitorInfo(),
            mpDirectDraw(),
            mpPrimarySurface(),
            mpBackBufferSurface(),
            mpDirect3D(),
            mpDirect3DDevice(),
            maLastUpdate(),
            maDeviceDesc(),
            maVertexCache(),
            mnCount(0),
            mnBeginSceneCount(0),
            mbPageFlipping( false ),
            mbHasNoTearingBlt( false ),
            mbError( false ),
            meType( PRIMITIVE_TYPE_UNKNOWN ),
            mpTexture(),
            maPageSize()
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            if(!(create(rWindow)))
            {
                throw lang::NoSupportException(
                    ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                                         "Could not create DirectX device!") ),NULL);
            }

            // allocate a single texture surface which can be used later.
            // we also use this to calibrate the page size.
            ::basegfx::B2IVector aPageSize(
                ::std::min(
                    static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureWidth),
                    static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)),
                ::std::min(
                    static_cast<sal_uInt32>(maDeviceDesc.dwMaxTextureHeight),
                    static_cast<sal_uInt32>(MAX_TEXTURE_SIZE)));
            while(true)
            {
                mpTexture = ::canvas::ISurfaceSharedPtr(
                    new DXSurface(*this,aPageSize));
                if(mpTexture->isValid())
                    break;

                aPageSize.setX(aPageSize.getX()>>1);
                aPageSize.setY(aPageSize.getY()>>1);
                if((aPageSize.getX() < MIN_TEXTURE_SIZE) ||
                   (aPageSize.getY() < MIN_TEXTURE_SIZE))
                {
                    throw lang::NoSupportException(
                        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                                            "Could not create DirectX device!") ),NULL);
                }
            }
            maPageSize=aPageSize;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::create
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::create( const ::Window& rWindow )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            maVertexCache.reserve(1024);

            mpWindow.reset( 
                new SystemChildWindow(
                const_cast<Window *>(&rWindow), 0) );

            // system child window must not receive mouse events
            mpWindow->SetMouseTransparent( sal_True );
            
            // parent should receive paint messages as well
            // [PARENTCLIPMODE_NOCLIP], the argument is here
            // passed as plain numeric value since the stupid
            // define utilizes a USHORT cast.
            mpWindow->SetParentClipMode(0x0002);
            
            // the system child window must not clear its background
            mpWindow->EnableEraseBackground( sal_False );

            mpWindow->SetControlForeground();
            mpWindow->SetControlBackground();
            mpWindow->EnablePaint(sal_False);

            const SystemEnvData *pData = mpWindow->GetSystemData();
            const HWND hwnd(reinterpret_cast<HWND>(pData->hWnd));
            mhWnd = const_cast<HWND>(hwnd);

            ENSURE_OR_THROW( IsWindow( reinterpret_cast<HWND>(mhWnd) ),
                              "DXRenderModuleDXRenderModuleWin32() No valid HWND given." );

            // retrieve position and size of the parent window
            const ::Size &rSizePixel(rWindow.GetSizePixel());

            // remember the size of the parent window, since we
            // need to use this for our child window.
            maSize.setX(static_cast<sal_Int32>(rSizePixel.Width()));
            maSize.setY(static_cast<sal_Int32>(rSizePixel.Height()));

            // let the child window cover the same size as the parent window.
            mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY());

            MonitorList aMonitorList;
            fillMonitorList( aMonitorList );
            
             mpDirectDraw = COMReference<IDirectDraw2>(
                createDirectDraw(aMonitorList, maMonitorInfo, mhWnd));

            if(!mpDirectDraw.is())
                return false;

            if( !queryCaps() )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): GetCaps failed" );
                mpDirectDraw.reset();
                return false;
            }

            if( !validateCaps() )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): Insufficient DirectX capabilities, failed" );
                mpDirectDraw.reset();
                return false;
            }

            if( FAILED( mpDirectDraw->SetCooperativeLevel( mhWnd,
                                                           DDSCL_NORMAL|DDSCL_MULTITHREADED|DDSCL_FPUPRESERVE ) ) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): SetCooperativeLevel failed" );
                mpDirectDraw.reset();
                return false;
            }

            // setup query struct
            rtl_fillMemory( &maSelectedFullscreenMode.selectedDesc,
                            sizeof(DDSURFACEDESC), 0 );
            maSelectedFullscreenMode.selectedDesc.dwSize = sizeof(DDSURFACEDESC);

            // read current display mode, e.g. for screen dimension
            if( FAILED( mpDirectDraw->GetDisplayMode( &maSelectedFullscreenMode.selectedDesc )) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): GetDisplayMode failed" );
                mpDirectDraw.reset();
                return false;
            }

            // check for supported primary surface formats...
            unsigned int nDisplayFormat = getDisplayFormat() & 0x00000FFF;
            if(nDisplayFormat != 0x888 && nDisplayFormat != 0x565) 
            {    
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): Unsupported DisplayFormat" );
                mpDirectDraw.reset();
                return false;
            }

            // create primary surface reference
            DDSURFACEDESC 		aSurfaceDesc;
            IDirectDrawSurface* pPrimarySurface;

            rtl_fillMemory( &aSurfaceDesc,
                            sizeof(DDSURFACEDESC), 0 );
            aSurfaceDesc.dwSize = sizeof(aSurfaceDesc);
            aSurfaceDesc.dwFlags = DDSD_CAPS;
            aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;

            if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pPrimarySurface, NULL)) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): CreateSurface failed" );
                mpDirectDraw.reset();
                return false;
            }

            mpPrimarySurface = COMReference< IDirectDrawSurface >(pPrimarySurface);

            // create a Clipper and associate it with the primary surface
            // and the render window
            LPDIRECTDRAWCLIPPER pClipper;
            if( FAILED(mpDirectDraw->CreateClipper( 0, &pClipper, NULL )) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): CreateClipper failed" );
                mpPrimarySurface.reset();
                mpDirectDraw.reset();
                return false;
            }
            if( FAILED(pClipper->SetHWnd(0, mhWnd)) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): Clipper->SetHWnd failed" );
                pClipper->Release();
                mpPrimarySurface.reset();
                mpDirectDraw.reset();
                return false;
            }
            if( FAILED(mpPrimarySurface->SetClipper( pClipper )) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): SetClipper failed" );
                pClipper->Release();
                mpPrimarySurface.reset();
                mpDirectDraw.reset();
                return false;
            }

            // clipper is now owned by mpPrimarySurface, release our reference
            pClipper->Release();

            // TODO(F3): Check whether palette needs any setup here

            // get us a backbuffer for simulated flipping
            IDirectDrawSurface* pSurface;

            // Strictly speaking, we don't need a full screen worth of
            // backbuffer here. We could also scale dynamically with
            // the current window size, but this will make it
            // necessary to temporarily have two buffers while copying
            // from the old to the new one. What's more, at the time
            // we need a larger buffer, DX might not have sufficient
            // resources available, and we're then left with too small
            // a back buffer, and no way of falling back to a
            // different canvas implementation.
            const ::basegfx::B2ISize aSize( getFramebufferSize() );

            rtl_fillMemory( &aSurfaceDesc,
                            sizeof(DDSURFACEDESC), 0 );
            aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
            aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
            aSurfaceDesc.dwHeight= aSize.getY();
            aSurfaceDesc.dwWidth = aSize.getX();

            aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;

            HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);

            if( FAILED( nRes ) )
            {
                if( nRes == DDERR_OUTOFVIDEOMEMORY )
                {
                    // local vid mem failed. Maybe AGP mem works?
                    aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
                    if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) )
                    {
                        // no chance, go defunct, and exit
                        VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
                        mpPrimarySurface.reset();
                        mpDirectDraw.reset();
                        return false;
                    }

                    VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" );
                }
                else
                {
                    // no chance, go defunct, and exit
                    VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
                    mpPrimarySurface.reset();
                    mpDirectDraw.reset();
                    return false;
                }
            }

            VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel",
                           aSurfaceDesc.dwWidth,
                           aSurfaceDesc.dwHeight );

            mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface);
            clearSurface(mpBackBufferSurface);

            if( !setup3DDevice() )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::Device(): setup3DDevice failed" );
                mpBackBufferSurface.reset();
                mpPrimarySurface.reset();
                mpDirectDraw.reset();
                return false;
            }

            mpWindow->Show();

            return true;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::getSize
        //////////////////////////////////////////////////////////////////////////////////

        ::basegfx::B2ISize DXRenderModule::getFramebufferSize() const
        {
            return mpDirectDraw.is() ?
                ::basegfx::B2ISize( maSelectedFullscreenMode.selectedDesc.dwWidth,
                                    maSelectedFullscreenMode.selectedDesc.dwHeight ) :
                ::basegfx::B2ISize();
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::setup3DDevice
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::setup3DDevice()
        {
            // create and setup 3D device
            // ==========================
            LPDIRECT3D2	pDirect3D;
            if( FAILED( mpDirectDraw->QueryInterface( IID_IDirect3D2, (LPVOID*)&pDirect3D ) ) )
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::setup3DDevice(): QueryInterface() for Direct3D failed" );
                return false;
            }

            mpDirect3D = COMReference< IDirect3D2 >(pDirect3D);

            LPDIRECT3DDEVICE2 pDirect3DDevice;
            // try HW-accelerated device first
            if( FAILED(mpDirect3D->CreateDevice( IID_IDirect3DHALDevice, 
                                                 mpBackBufferSurface.get(), 
                                                 &pDirect3DDevice )) )
            {
                // no HW 3D support - go defunct, and exit
                VERBOSE_TRACE( "Device::setup3DDevice(): CreateDevice() for HW Direct3D rendering failed" );
                mpDirect3D.reset();
                return false;
            }

            D3DDEVICEDESC aHELDeviceDesc;
            rtl_fillMemory(&maDeviceDesc,sizeof(maDeviceDesc),0);
            rtl_fillMemory(&aHELDeviceDesc,sizeof(aHELDeviceDesc),0);
            maDeviceDesc.dwSize = sizeof(maDeviceDesc);
            aHELDeviceDesc.dwSize = sizeof(aHELDeviceDesc);
            if(FAILED(pDirect3DDevice->GetCaps(&maDeviceDesc,&aHELDeviceDesc))) 
            {
                // go defunct, and exit
                VERBOSE_TRACE( "Device::setup3DDevice(): GetCaps() for Direct3DDevice failed" );
                mpDirect3D.reset();
                return false;
            }

            mpDirect3DDevice = COMReference< IDirect3DDevice2 >(pDirect3DDevice);

            // select appropriate texture format (_need_ alpha channel here)
            rtl_fillMemory( &maTextureFormat,
                            sizeof(DDPIXELFORMAT), 0 );
            maTextureFormat.dwSize = sizeof(DDPIXELFORMAT);
            if( SUCCEEDED(mpDirect3DDevice->EnumTextureFormats( EnumTextureFormatsCallback, &maTextureFormat )) )
            {
                bool bSupportedFormat = true;
                if((maTextureFormat.dwFlags & (DDPF_ALPHAPIXELS | DDPF_RGB)) != (DDPF_ALPHAPIXELS | DDPF_RGB))
                    bSupportedFormat = false;
                else if(maTextureFormat.dwRGBAlphaBitMask != 0xFF000000)
                    bSupportedFormat = false;
                else if(maTextureFormat.dwRBitMask != 0x00FF0000)
                    bSupportedFormat = false;
                else if(maTextureFormat.dwGBitMask != 0x0000FF00)
                    bSupportedFormat = false;
                else if(maTextureFormat.dwBBitMask != 0x000000FF)
                    bSupportedFormat = false;

                if(bSupportedFormat) 
                {
                    VERBOSE_TRACE( "Device::setup3DDevice(): chose texture format dwRGBBitCount %d, dwRBitMask %x, "
                                   "dwGBitMask %x, dwBBitMask %x and dwRGBAlphaBitMask %x. The texture uses %s alpha.",
                                   maTextureFormat.dwRGBBitCount,
                                   maTextureFormat.dwRBitMask,
                                   maTextureFormat.dwGBitMask,
                                   maTextureFormat.dwBBitMask,
                                   maTextureFormat.dwRGBAlphaBitMask,
                                   maTextureFormat.dwFlags & DDPF_ALPHAPREMULT ? "premultiplied" : "non-premultiplied" );
                    
                    // setup the device (with as much as we can possibly do here)
                    // ==========================================================

                    LPDIRECT3DVIEWPORT2	pViewport;

                    if( SUCCEEDED(mpDirect3D->CreateViewport( &pViewport, NULL )) )
                    {
                        if( SUCCEEDED(mpDirect3DDevice->AddViewport( pViewport )) )
                        {
                            // setup viewport (to whole backbuffer)
                            D3DVIEWPORT2 aViewport;

                            aViewport.dwSize = sizeof(D3DVIEWPORT2);
                            aViewport.dwX = 0;
                            aViewport.dwY = 0;
                            aViewport.dwWidth = maSelectedFullscreenMode.selectedDesc.dwWidth;
                            aViewport.dwHeight = maSelectedFullscreenMode.selectedDesc.dwHeight;
                            aViewport.dvClipX = -1.0;
                            aViewport.dvClipY =  -1.0;
                            aViewport.dvClipWidth  = 2.0;
                            aViewport.dvClipHeight = 2.0;
                            aViewport.dvMinZ = 0.0;
                            aViewport.dvMaxZ = 1.0;

                            if( SUCCEEDED(pViewport->SetViewport2( &aViewport )) )
                            {
                                if( SUCCEEDED(mpDirect3DDevice->SetCurrentViewport( pViewport )) )
                                {
                                    // Viewport was handed over to 3DDevice, thus we can release now
                                    pViewport->Release();

                                    // currently, no need for any
                                    // matrix or light source
                                    // setup, since we only render
                                    // transformed&lighted
                                    // vertices

                                    // done; successfully
                                    return true;
                                }
                                else
                                {
                                    VERBOSE_TRACE( "Device::setup3DDevice(): SetCurrentViewport failed" );
                                }
                            }
                            else
                            {
                                VERBOSE_TRACE( "Device::setup3DDevice(): SetViewport2 failed" );
                            }
                        }
                        else
                        {
                            VERBOSE_TRACE( "Device::setup3DDevice(): AddViewport failed" );
                        }

                        pViewport->Release();
                    }
                    else
                    {
                        VERBOSE_TRACE( "Device::setup3DDevice(): CreateViewport failed" );
                    }
                }
                else
                {
                    VERBOSE_TRACE( "Device::setup3DDevice(): No supported pixelformat" );
                }
            }
            else
            {
                VERBOSE_TRACE( "Device::setup3DDevice(): EnumTextureFormats failed" );
            }

            // go defunct, and exit
            mpDirect3DDevice.reset();
            mpDirect3D.reset();

            return false;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::queryCaps
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::queryCaps()
        {
            DDCAPS	aHWCaps;
            DDCAPS	aHELCaps;

            rtl_fillMemory( &aHWCaps,
                            sizeof(aHWCaps), 0 );
            rtl_fillMemory( &aHELCaps,
                            sizeof(aHELCaps), 0 );
            aHWCaps.dwSize = sizeof( aHWCaps );
            aHELCaps.dwSize = sizeof( aHELCaps );

            if( FAILED( mpDirectDraw->GetCaps( &aHWCaps,
                                            &aHELCaps ) ) )
            {
                return false;
            }

            mbHasNoTearingBlt = aHWCaps.dwFXCaps & DDBLTFX_NOTEARING;

            VERBOSE_TRACE( "dxcanvas initialization: %d bytes VRAM free for surfaces (%d with AGP mem), "
                           "%d bytes VRAM free for textures (%d with AGP mem)", 
                           getAvailableSurfaceMem( false ),
                           getAvailableSurfaceMem( true ),
                           getAvailableTextureMem( false ),
                           getAvailableTextureMem( true ) );
            
            return true;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::validateCaps
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::validateCaps()
        {
            // TODO(E3): Validate HW capabilities. Depending on primary
            // surface size, reject HW e.g. on the grounds of insufficient
            // VRAM.

            // setup query struct
            DDSURFACEDESC desc;
            rtl_fillMemory(&desc,sizeof(DDSURFACEDESC),0);
            desc.dwSize = sizeof(DDSURFACEDESC);

            // read current display mode, e.g. for screen dimension
            if(FAILED( mpDirectDraw->GetDisplayMode(&desc)))
                return false;

            // simple heuristic: we need at least 3 times the desktop
            // resolution based on ARGB color values...
            std::size_t nMinimumVRAMSize = ((desc.dwWidth*desc.dwHeight)<<2)*3;
            if(getAvailableSurfaceMem() < nMinimumVRAMSize)
                return false;

            return true;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::getDisplayFormat
        //////////////////////////////////////////////////////////////////////////////////

        unsigned int DXRenderModule::getDisplayFormat() const 
        {
            unsigned int nFormat;
            nFormat  = ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRGBAlphaBitMask)<<12;
            nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwRBitMask)<<8;
            nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwGBitMask)<<4;
            nFormat |= ::canvas::tools::bitcount32(maSelectedFullscreenMode.selectedDesc.ddpfPixelFormat.dwBBitMask);
            return nFormat;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::getAvailableSurfaceMem
        //////////////////////////////////////////////////////////////////////////////////

        std::size_t DXRenderModule::getAvailableSurfaceMem( bool bWithAGPMem ) const
        {
            if( !mpDirectDraw.is() )
                return 0;

            std::size_t nRes( 0 );

            DDSCAPS aSurfaceCaps;
            DWORD	nTotal, nFree;

            // real VRAM (const_cast, since GetAvailableVidMem is non-const)
            aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
            if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
                return 0;

            nRes += nFree;

            if( bWithAGPMem )
            {
                // AGP RAM (const_cast, since GetAvailableVidMem is non-const)
                aSurfaceCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
                if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
                    return 0;
                
                nRes += nFree;
            }

            return nRes;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::getAvailableTextureMem
        //////////////////////////////////////////////////////////////////////////////////

        std::size_t DXRenderModule::getAvailableTextureMem( bool bWithAGPMem ) const
        {
            if( !mpDirectDraw.is() )
                return 0;

            std::size_t nRes( 0 );

            DDSCAPS aSurfaceCaps;
            DWORD	nTotal, nFree;

            // TODO(F1): Check if flags are applicable

            // real VRAM (const_cast, since GetAvailableVidMem is non-const)
            aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
            if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
                return 0;

            nRes += nFree;

            if( bWithAGPMem )
            {
                // AGP RAM (const_cast, since GetAvailableVidMem is non-const)
                aSurfaceCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
                if( FAILED(const_cast<IDirectDraw2&>(*mpDirectDraw).GetAvailableVidMem( &aSurfaceCaps, &nTotal, &nFree )) )
                    return 0;
                
                nRes += nFree;
            }

            // TODO(F1): Add pool mem

            return nRes;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::convert2Screen
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::convert2Screen( ::basegfx::B2IPoint& io_rDestPos,
                                             ::basegfx::B2IRange& io_rDestArea )
        {
            POINT aPoint = { 0, 0 };
            ClientToScreen( mhWnd, &aPoint );
 
            // i52230 make sure given screen coordinate is relative to
            // this monitor's area (the device rendering is always
            // contained to a single monitor)
            aPoint.x -= maMonitorInfo.rcMonitor.left;
            aPoint.y -= maMonitorInfo.rcMonitor.top;
 
            io_rDestPos.setX( io_rDestPos.getX() + aPoint.x );
            io_rDestPos.setY( io_rDestPos.getY() + aPoint.y );

            const ::basegfx::B2ISize& rSize( getFramebufferSize() );

            // calc output bounds (clip against framebuffer bounds)
            io_rDestArea = ::basegfx::B2IRange(
                ::std::max( sal_Int32(0),
                            ::std::min( sal_Int32(rSize.getX()),
                                        sal_Int32(io_rDestArea.getMinX() + aPoint.x) ) ),
                ::std::max( sal_Int32(0),
                            ::std::min( sal_Int32(rSize.getY()),
                                        sal_Int32(io_rDestArea.getMinY() + aPoint.y) ) ),
                ::std::max( sal_Int32(0),
                            ::std::min( sal_Int32(rSize.getX()),
                                        sal_Int32(io_rDestArea.getMaxX() + aPoint.x) ) ),
                ::std::max( sal_Int32(0),
                            ::std::min( sal_Int32(rSize.getY()),
                                        sal_Int32(io_rDestArea.getMaxY() + aPoint.y) ) ) );
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::createSystemMemorySurface
        //////////////////////////////////////////////////////////////////////////////////

        COMReference<IDirectDrawSurface> DXRenderModule::createSystemMemorySurface( const ::basegfx::B2IVector& rSize )
        {
            DDSURFACEDESC 		aSurfaceDesc;
            IDirectDrawSurface* pSurface;

            aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
            aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;;
            aSurfaceDesc.dwWidth = rSize.getX();
            aSurfaceDesc.dwHeight= rSize.getY();

            rtl_copyMemory( &aSurfaceDesc.ddpfPixelFormat, &maTextureFormat, sizeof(DDPIXELFORMAT) );

            aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
     
            HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);
            if(FAILED(nRes))
                return COMReference<IDirectDrawSurface>(NULL);

            return COMReference<IDirectDrawSurface>(pSurface);
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::flip
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::flip( const ::basegfx::B2IRectangle& rUpdateArea,
                                   const ::basegfx::B2IRectangle& rCurrWindowArea )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            // see if the main surfaces got lost. if so, try to
            // restore them. bail out if this operation fails.
            if(!(validateMainSurfaces()))
                return false;

            flushVertexCache();

            ENSURE_OR_THROW( !mnBeginSceneCount,
                              "Device::flip(): within 3D scene" );

            // TODO(E3): handle DX errors more thoroughly. For fullscreen
            // exclusive mode, actually even our primary surface can get
            // lost and needs restore!

            if( mpDirectDraw.is() &&
                mpPrimarySurface.is() &&
                mpBackBufferSurface.is() )
            {
                // ignore area and offset for page flipping device
                if( mbPageFlipping )
                {
#if defined(VERBOSE) && defined(DBG_UTIL)
                    renderFPSCounter();
                    renderMemAvailable();
#endif
                    VERBOSE_TRACE( "Device::flip(): Using true page flipping" );
                    
                    // use true page flipping. Hopefully, the 3D hardware
                    // is flushed on this flip call (rumours have it that
                    // way), otherwise, perform the Lock hack as for the
                    // Blt below.
                    if( SUCCEEDED(mpPrimarySurface->Flip( NULL, DDFLIP_WAIT )) )
                        return true;
                }
                else
                {
                    VERBOSE_TRACE( "Device::flip(): Using blt for page flipping" );

                    // determine actual window position
                    ::basegfx::B2IPoint aDestPoint( rUpdateArea.getMinimum() );
                    ::basegfx::B2IRange aSourceArea( rUpdateArea );
                    ::basegfx::B2IRange aDestArea( 0,0,
                                                   static_cast<sal_Int32>(rCurrWindowArea.getWidth()),
                                                   static_cast<sal_Int32>(rCurrWindowArea.getHeight()) );
                    convert2Screen( aDestPoint, aDestArea );

                    // perform clipping
                    if( !::canvas::tools::clipBlit( aSourceArea,
                                                    aDestPoint,
                                                    rUpdateArea,
                                                    aDestArea ) )
                        return true; // fully clipped, but still, in a way,
                                     // successful.

                    // TODO(P1): Rumours have it that the 3D hardware
                    // _might_ still be rendering with flaky drivers,
                    // which don't flush properly on Blt(). It was said,
                    // that 'usually', it works to lock the 3D render
                    // target (the backbuffer in this case). OTOH, I've
                    // found that this tends to degrade performance
                    // significantly on complying cards...
                    
                    // TODO(P1): Up until rev. 1.3, this method contained
                    // code to make sure the blit will start _immediately_
                    // after the Blt call. If this is not warranted, wait
                    // for the next vsync. As this case was found to be
                    // extremely seldom, kicked out (what's more, there's
                    // simply no guarantee that the blitter will be
                    // available at any point in the code - Windows still
                    // is a preemptive multi-processing environment. And
                    // _if_ we're competing with someone over the blitter,
                    // we will do so the next VBLANK interval, and the
                    // following...)

                    // screen update seems to be smoother when waiting
                    // for vblank in every case - even when blitter
                    // supports the DDBLTFX_NOTEARING flag.
                    if( FAILED(mpDirectDraw->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,
                                                                  NULL)) )
                        return false;

                    DDBLTFX  aBltFx;
                    DDBLTFX* pBltFX = NULL;
                    if( mbHasNoTearingBlt )
                    {
                        // Blt can internally schedule for no-tearing   
                        // ===========================================  
                        
                        rtl_fillMemory( &aBltFx,
                                        sizeof(aBltFx), 0 );
                        aBltFx.dwSize = sizeof(aBltFx);
                        aBltFx.dwDDFX = DDBLTFX_NOTEARING;
                        
                        pBltFX = &aBltFx;
                    }

                    if( doBlit( aDestPoint,
                                *mpPrimarySurface,
                                aSourceArea,
                                *mpBackBufferSurface,
                                pBltFX,false ) )
                    {
#if defined(VERBOSE) && defined(DBG_UTIL)
                        renderFPSCounter();
                        renderMemAvailable();
#endif
                        return true;
                    }
                }
            }
            return false;
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::disposing
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::disposing()
        {
            if(!(mhWnd))
                return;

            mpTexture.reset();
            mpWindow.reset();
            mhWnd=NULL;

            // refrain from releasing the DX5 objects - deleting the
            // DX5 device seems to kill the whole engine, including
            // all objects we might still hold references to
            // (surfaces, e.g.)
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::screenshot
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::screenShot()
        {
            if(!(mpBackBufferSurface.get()))
                return;
            char filename[256];
            static sal_uInt32 counter = 0;
            sprintf(filename,"c:\\shot%d.bmp",counter++);
            dumpSurface(mpBackBufferSurface,filename);
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::validateMainSurfaces
        //////////////////////////////////////////////////////////////////////////////////

        bool DXRenderModule::validateMainSurfaces() 
        {
            if(mpPrimarySurface.get()) {
                if(mpPrimarySurface->IsLost() == DDERR_SURFACELOST) {
                    if(FAILED(mpPrimarySurface->Restore()))
                        return false;
                }
            }

            if(mpBackBufferSurface.get()) {
                if(mpBackBufferSurface->IsLost() == DDERR_SURFACELOST) 
                {	
                    // TODO(F1): simply restoring the backbuffer does not
                    // work as expected, we need to re-create everything
                    // from scratch. find out why...
                    //if(SUCCEEDED(mpBackBufferSurface->Restore()))
                    //	return setup3DDevice();

                    mpBackBufferSurface.reset();

                    // get us a backbuffer for simulated flipping
                    IDirectDrawSurface* pSurface;

                    // TODO(P2): Strictly speaking, we don't need a full screen worth of 
                    // backbuffer here. We could also scale dynamically with the current
                    // window size, but this will make it necessary to temporarily have two
                    // buffers while copying from the old to the new one. YMMV.
                    const ::basegfx::B2ISize aSize( getFramebufferSize() );

                    DDSURFACEDESC aSurfaceDesc;
                    rtl_fillMemory( &aSurfaceDesc, sizeof(DDSURFACEDESC), 0 );
                    aSurfaceDesc.dwSize = sizeof(DDSURFACEDESC);
                    aSurfaceDesc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
                    aSurfaceDesc.dwHeight= aSize.getY();
                    aSurfaceDesc.dwWidth = aSize.getX();

                    aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;

                    HRESULT nRes = mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL);

                    if( FAILED( nRes ) )
                    {
                        if( nRes == DDERR_OUTOFVIDEOMEMORY )
                        {
                            // local vid mem failed. Maybe AGP mem works?
                            aSurfaceDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE | DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM;
                            if( FAILED(mpDirectDraw->CreateSurface(&aSurfaceDesc, &pSurface, NULL)) )
                            {
                                // no chance
                                return false;
                            }

                            VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer reverted to non-local video mem" );
                        }
                        else
                        {
                            // no chance
                            VERBOSE_TRACE( "Device::Device(): CreateSurface for backbuffer failed" );
                            return false;
                        }
                    }

                    VERBOSE_TRACE( "Device::Device(): created backbuffer of size %d times %d pixel",
                                   aSurfaceDesc.dwWidth,
                                   aSurfaceDesc.dwHeight );

                    mpBackBufferSurface = COMReference< IDirectDrawSurface >(pSurface);

                    return setup3DDevice();
                }
            }

            return true;
        }

        void DXRenderModule::renderInfoText( const ::rtl::OUString& rStr,
                                             const Gdiplus::PointF& rPos ) const
        {
            ENSURE_OR_THROW( !mnBeginSceneCount,
                              "Device::renderInfoText(): within 3D scene" );

            // render text directly to primary surface
            GraphicsSharedPtr pGraphics;

            if( mbPageFlipping )
            {
                // render on top of backbuffer. We have 
                // page flipping, anyway, thus this will
                // cost us nothing.
                pGraphics = createSurfaceGraphics( mpBackBufferSurface );
            }
            else
            {
                // render FPS directly to front buffer. 
                // That saves us another explicit blit,
                // and for me, the FPS counter can blink, 
                // if it likes to...
                pGraphics = createSurfaceGraphics( mpPrimarySurface );
            }

            if( !mbPageFlipping )
            {
                // clear background. We might be doing optimized redraws,
                // and the background under the FPS count will then not be
                // cleared.
                Gdiplus::SolidBrush aBrush( 
                    Gdiplus::Color( 255, 255, 255 ) );

                pGraphics->FillRectangle( &aBrush,
                                          rPos.X, rPos.Y, 80.0, 20.0 );
            }

            Gdiplus::SolidBrush aBrush( 
                Gdiplus::Color( 255, 0, 255 ) );
            Gdiplus::Font aFont( NULL, 
                                 16,
                                 Gdiplus::FontStyleRegular,
                                 Gdiplus::UnitWorld,
                                 NULL );
            pGraphics->DrawString( reinterpret_cast<LPCWSTR>(rStr.getStr()),
                                   rStr.getLength(),
                                   &aFont,
                                   rPos,
                                   &aBrush );
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::renderMemAvailable
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::renderMemAvailable() const
        {
            ENSURE_OR_THROW( !mnBeginSceneCount,
                              "DXRenderModule::renderMemAvailable(): within 3D scene" );

            const double nSurfaceMem( getAvailableSurfaceMem()/1024 );
                            
            ::rtl::OUString text( ::rtl::math::doubleToUString( nSurfaceMem,
                                                                rtl_math_StringFormat_F,
                                                                2,'.',NULL,' ') );

            // pad with leading space
            while( text.getLength() < 6 )
                text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;

            text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("S: ")) + text;

            renderInfoText( text,
                            Gdiplus::PointF( 0.0, 20) );


            const double nTexMem( getAvailableTextureMem()/1024 );

            text = ::rtl::math::doubleToUString( nTexMem,
                                                rtl_math_StringFormat_F,
                                                2,'.',NULL,' ');
            // pad with leading space
            while( text.getLength() < 6 )
                text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;

            text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("T: ")) + text;

            renderInfoText( text,
                            Gdiplus::PointF( 0.0, 40) );

            VERBOSE_TRACE( "dxcanvas: %f free surface mem, %f free texture mem", 
                           nSurfaceMem, nTexMem );
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::renderFPSCounter
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::renderFPSCounter() const
        {
            ENSURE_OR_THROW( !mnBeginSceneCount,
                              "DXRenderModule::ren    derFPSCounter(): within 3D scene" );

            const double denominator( maLastUpdate.getElapsedTime() );
            maLastUpdate.reset();
                            
            ::rtl::OUString text( ::rtl::math::doubleToUString( denominator == 0.0 ? 100.0 : 1.0/denominator,
                                                                rtl_math_StringFormat_F,
                                                                2,'.',NULL,' ') );

            // pad with leading space
            while( text.getLength() < 6 )
                text = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" ")) + text;

            text += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (" fps"));

            renderInfoText( text,
                            Gdiplus::PointF() ); 

            VERBOSE_TRACE( "dxcanvas: %f FPS", 
                           denominator == 0.0 ? 100.0 : 1.0/denominator );
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::resize
        //////////////////////////////////////////////////////////////////////////////////

        void DXRenderModule::resize( const ::basegfx::B2IRange& rect )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            if( mhWnd==0 )
                return;

            // don't do anything if the size didn't change.
            if(maSize.getX() == static_cast<sal_Int32>(rect.getWidth()) &&
               maSize.getY() == static_cast<sal_Int32>(rect.getHeight()))
               return;

            // TODO(Q2): use numeric cast to prevent overflow
            maSize.setX(static_cast<sal_Int32>(rect.getWidth()));
            maSize.setY(static_cast<sal_Int32>(rect.getHeight()));

            mpWindow->SetPosSizePixel(0,0,maSize.getX(),maSize.getY());
        }

        //////////////////////////////////////////////////////////////////////////////////
        // DXRenderModule::getPageSize
        //////////////////////////////////////////////////////////////////////////////////

        ::basegfx::B2IVector DXRenderModule::getPageSize()
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );
            return maPageSize;
        }

        ::canvas::ISurfaceSharedPtr DXRenderModule::createSurface( const ::basegfx::B2IVector& surfaceSize )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            const ::basegfx::B2IVector& rPageSize( getPageSize() );
            ::basegfx::B2ISize aSize(surfaceSize);
            if(!(aSize.getX()))
                aSize.setX(rPageSize.getX());
            if(!(aSize.getY()))
                aSize.setY(rPageSize.getY());

            if(mpTexture.use_count() == 1)
                return mpTexture;

            return ::canvas::ISurfaceSharedPtr( 
                new DXSurface(*this,
                              aSize) );
        }

        void DXRenderModule::beginPrimitive( PrimitiveType eType )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            ENSURE_OR_THROW( !mnBeginSceneCount,
                              "DXRenderModule::beginPrimitive(): nested call" );

            ++mnBeginSceneCount;
            meType=eType;
            mnCount=0;
        }

        void DXRenderModule::endPrimitive()
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            --mnBeginSceneCount;
            meType=PRIMITIVE_TYPE_UNKNOWN;
            mnCount=0;
        }

        void DXRenderModule::pushVertex( const ::canvas::Vertex& vertex )
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            switch(meType)
            {
                case PRIMITIVE_TYPE_TRIANGLE:
                {
                    maVertexCache.push_back(vertex);
                    ++mnCount;
                    mnCount &= 3;
                    break;
                }

                case PRIMITIVE_TYPE_QUAD:
                {
                    if(mnCount == 3)
                    {
                        const std::size_t size(maVertexCache.size());
                        ::canvas::Vertex v0(maVertexCache[size-1]);
                        ::canvas::Vertex v2(maVertexCache[size-3]);
                        maVertexCache.push_back(v0);
                        maVertexCache.push_back(vertex);
                        maVertexCache.push_back(v2);
                        mnCount=0;
                    }
                    else
                    {
                        maVertexCache.push_back(vertex);
                        ++mnCount;
                    }
                    break;
                }

                default:
                    OSL_FAIL( "DXRenderModule::pushVertex(): unexpected primitive types" );
                    break;
            }
        }

        bool DXRenderModule::isError()
        {
            // TODO(P2): get rid of those fine-grained locking
            ::osl::MutexGuard aGuard( maMutex );

            return mbError;
        }

        void DXRenderModule::flushVertexCache()
        {
            if(!(maVertexCache.size()))
                return;

            mbError=true;

            if( FAILED(mpDirect3DDevice->BeginScene()) )
                return;

            // enable texture alpha blending
            if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE,TRUE)))
                return;

            // enable texture alpha modulation, for honoring fAlpha
            if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND,
                                                        D3DTBLEND_MODULATEALPHA)) )
                return;

            // enable texture magnification filtering (don't care if this
            // fails, it's just visually more pleasant)
            mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMAG,
                                             D3DFILTER_LINEAR);

            // enable texture minification filtering (don't care if this
            // fails, it's just visually more pleasant)
            mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_TEXTUREMIN,
                                             D3DFILTER_LINEAR);

            // enable subpixel texture output (don't care if this
            // fails, it's just visually more pleasant)
             mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SUBPIXEL,
                                             TRUE);

            // normal combination of object...
            if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,
                                                        D3DBLEND_SRCALPHA)) )
                return;

            // ..and background color
            if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,
                                                        D3DBLEND_INVSRCALPHA)) )
                return;

            // disable backface culling; this enables us to mirror sprites
            // by simply reverting the triangles, which, with enabled
            // culling, would be invisible otherwise
            if( FAILED(mpDirect3DDevice->SetRenderState(D3DRENDERSTATE_CULLMODE,
                                                        D3DCULL_NONE)) )
                return;

            mbError=false;

             const float nHalfPixelSizeX(0.5f/maPageSize.getX());
             const float nHalfPixelSizeY(0.5f/maPageSize.getY());
            sal_uInt32 nIndex(0);
            const std::size_t size(maVertexCache.size());
            D3DTLVERTEX *vertices = static_cast<D3DTLVERTEX *>(_alloca(sizeof(D3DTLVERTEX)*size));
            vertexCache_t::const_iterator it(maVertexCache.begin());
            while(it != maVertexCache.end())
            {
                vertices[nIndex++] = D3DTLVERTEX(
                    D3DVECTOR(static_cast<D3DVALUE>(it->x),
                              static_cast<D3DVALUE>(it->y),
                              static_cast<D3DVALUE>(it->z)),
                    1,
                    D3DRGBA(1,1,1,it->a),
                    D3DRGBA(0,0,0,0),
                    static_cast<float>(it->u + nHalfPixelSizeX),
                    static_cast<float>(it->v + nHalfPixelSizeY));
                ++it;
            }

            maVertexCache.clear();

            mbError |= FAILED(mpDirect3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 
                                                              D3DVT_TLVERTEX,
                                                              (LPVOID)vertices,
                                                              size,
                                                              0));

            mbError |= FAILED(mpDirect3DDevice->EndScene());
        }
    }

    IDXRenderModuleSharedPtr createRenderModule( const ::Window& rParent )
    {
        return IDXRenderModuleSharedPtr( new DXRenderModule(rParent) );
    }
}

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
