home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 275 / DPCS0111DVD.ISO / Toolkit / Audio-Visual / VirtualDub / Source / VirtualDub-1.9.10-src.7z / src / Riza / source / displayddraw.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2009-09-24  |  41.2 KB  |  1,541 lines

  1. #include <windows.h>
  2. #include <ddraw.h>
  3. #include <vd2/system/vdtypes.h>
  4. #include <vd2/system/vdstl.h>
  5. #include <vd2/system/time.h>
  6. #include <vd2/system/math.h>
  7. #include <vd2/Kasumi/pixmap.h>
  8. #include <vd2/Kasumi/pixmapops.h>
  9. #include <vd2/Kasumi/pixmaputils.h>
  10. #include "displaydrv.h"
  11.  
  12. //#define VDDEBUG_DISP (void)sizeof printf
  13. #define VDDEBUG_DISP VDDEBUG
  14.  
  15. #if 0
  16.     #define DEBUG_LOG(x) VDLog(kVDLogInfo, VDStringW(L##x))
  17. #else
  18.     #define DEBUG_LOG(x)
  19. #endif
  20.  
  21. void VDDitherImage(VDPixmap& dst, const VDPixmap& src, const uint8 *pLogPal);
  22.  
  23. ///////////////////////////////////////////////////////////////////////////////////////////////////
  24.  
  25. class IVDDirectDrawClient {
  26. public:
  27.     virtual void DirectDrawShutdown() = 0;
  28.     virtual void DirectDrawPrimaryRestored() = 0;
  29. };
  30.  
  31. class IVDDirectDrawManager {
  32. public:
  33.     virtual IDirectDraw2 *GetDDraw() = 0;
  34.     virtual const DDCAPS& GetCaps() = 0;
  35.     virtual IDirectDrawSurface2 *GetPrimary() = 0;
  36.     virtual const DDSURFACEDESC& GetPrimaryDesc() = 0;
  37.     virtual HMONITOR GetMonitor() = 0;
  38.     virtual const vdrect32& GetMonitorRect() = 0;
  39.     virtual bool Restore() = 0;
  40. };
  41.  
  42. class VDDDrawPresentHistory {
  43. public:
  44.     bool mbPresentPending;
  45.     bool mbPresentBlitStarted;
  46.     float mPresentDelay;
  47.     float mVBlankSuccess;
  48.     uint64    mPresentStartTime;
  49.  
  50.     double    mAveragePresentTime;
  51.     double    mAverageStartScanline;
  52.     double    mAverageEndScanline;
  53.     uint32    mPollCount;
  54.     uint32    mLastBracketY1;
  55.     uint32    mLastBracketY2;
  56.  
  57.     float    mScanlineTarget;
  58.     sint32    mLastScanline;
  59.     bool    mbLastWasVBlank;
  60.  
  61.     sint32    mScanTop;
  62.     sint32    mScanBottom;
  63.  
  64.     float mSuccessProb[17];
  65.     float mAttemptProb[17];
  66.  
  67.     VDDDrawPresentHistory()
  68.         : mbPresentPending(false)
  69.         , mbPresentBlitStarted(false)
  70.         , mPresentDelay(0.f)
  71.         , mVBlankSuccess(1.0f)
  72.         , mPresentStartTime(0)
  73.         , mAveragePresentTime(0)
  74.         , mAverageStartScanline(0)
  75.         , mAverageEndScanline(0)
  76.         , mPollCount(0)
  77.         , mLastBracketY1(0)
  78.         , mLastBracketY2(0)
  79.         , mScanlineTarget(0)
  80.         , mLastScanline(0)
  81.         , mbLastWasVBlank(false)
  82.         , mScanTop(0)
  83.         , mScanBottom(0)
  84.     {
  85.         memset(&mSuccessProb, 0, sizeof mSuccessProb);
  86.         memset(&mAttemptProb, 0, sizeof mAttemptProb);
  87.     }
  88. };
  89.  
  90. class VDDirectDrawManager : public IVDDirectDrawManager {
  91. public:
  92.     bool Init(IVDDirectDrawClient *pClient);
  93.     void Shutdown(IVDDirectDrawClient *pClient);
  94.  
  95.     IDirectDraw2 *GetDDraw() { return mpdd; }
  96.     const DDCAPS& GetCaps() { return mCaps; }
  97.  
  98.     IDirectDrawSurface2 *GetPrimary() { return mpddsPrimary; }
  99.     const DDSURFACEDESC& GetPrimaryDesc() { return mPrimaryDesc; }
  100.  
  101.     HMONITOR GetMonitor() { return mhMonitor; }
  102.     const vdrect32& GetMonitorRect() { return mMonitorRect; }
  103.  
  104.     bool Restore();
  105.  
  106. protected:
  107.     bool InitPrimary();
  108.     void ShutdownPrimary();
  109.  
  110.  
  111.     int mInitCount;
  112.  
  113.     HMODULE                    mhmodDD;
  114.     HMONITOR                mhMonitor;
  115.  
  116.     IDirectDraw2            *mpdd;
  117.     IDirectDrawSurface2        *mpddsPrimary;
  118.  
  119.     DDSURFACEDESC            mPrimaryDesc;
  120.     DDCAPS                    mCaps;
  121.  
  122.     vdrect32                mMonitorRect;
  123.  
  124.     typedef vdfastvector<IVDDirectDrawClient *> tClients;
  125.     tClients mClients;
  126. };
  127.  
  128. bool VDDirectDrawManager::Init(IVDDirectDrawClient *pClient) {
  129.     if (mInitCount) {
  130.         ++mInitCount;
  131.         mClients.push_back(pClient);
  132.         return true;
  133.     }
  134.  
  135.     POINT pt = {0,0};
  136.     HMODULE hmodUser32 = GetModuleHandleA("user32");
  137.     typedef HMONITOR (WINAPI *tpMonitorFromPoint)(POINT, DWORD);
  138.     tpMonitorFromPoint pMonitorFromPoint = (tpMonitorFromPoint)GetProcAddress(hmodUser32, "MonitorFromPoint");
  139.     mhMonitor = NULL;
  140.     if (pMonitorFromPoint)
  141.         mhMonitor = pMonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
  142.  
  143.     mMonitorRect.set(0, 0, ::GetSystemMetrics(SM_CXSCREEN), ::GetSystemMetrics(SM_CYSCREEN));
  144.  
  145.     // GetMonitorInfo() requires Windows 98/2000.
  146.     if (mhMonitor) {
  147.         typedef BOOL (APIENTRY *tpGetMonitorInfo)(HMONITOR mon, LPMONITORINFO lpmi);
  148.         tpGetMonitorInfo pGetMonitorInfo = (tpGetMonitorInfo)GetProcAddress(GetModuleHandle("user32"), "GetMonitorInfo");
  149.  
  150.         if (pGetMonitorInfo) {
  151.             MONITORINFO monInfo = {sizeof(MONITORINFO)};
  152.             if (pGetMonitorInfo(mhMonitor, &monInfo))
  153.                 mMonitorRect.set(monInfo.rcMonitor.left, monInfo.rcMonitor.top, monInfo.rcMonitor.right, monInfo.rcMonitor.bottom);
  154.         }
  155.     }
  156.  
  157.     mhmodDD = LoadLibrary("ddraw");
  158.     if (!mhmodDD)
  159.         return false;
  160.  
  161.     do {
  162.         typedef HRESULT (WINAPI *tpDirectDrawCreate)(GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter);
  163.         tpDirectDrawCreate pDirectDrawCreate = (tpDirectDrawCreate)GetProcAddress(mhmodDD, "DirectDrawCreate");
  164.  
  165.         if (!pDirectDrawCreate)
  166.             break;
  167.  
  168.         IDirectDraw *pdd;
  169.         HRESULT hr;
  170.  
  171.         // create DirectDraw object
  172.         if (FAILED(pDirectDrawCreate(NULL, &pdd, NULL))) {
  173.             DEBUG_LOG("VideoDriver/DDraw: Couldn't create DirectDraw2 object\n");
  174.             break;
  175.         }
  176.  
  177.         // query up to IDirectDraw2 (DirectX 3)
  178.         hr = pdd->QueryInterface(IID_IDirectDraw2, (void **)&mpdd);
  179.         pdd->Release();
  180.  
  181.         if (FAILED(hr))
  182.             break;
  183.  
  184.         // get caps
  185.         memset(&mCaps, 0, sizeof mCaps);
  186.         mCaps.dwSize = sizeof(DDCAPS);
  187.         hr = mpdd->GetCaps(&mCaps, NULL);
  188.         if (FAILED(hr)) {
  189.             DEBUG_LOG("VideoDriver/DDraw: Couldn't get caps\n");
  190.             break;
  191.         }
  192.  
  193.         // set cooperative level
  194.         hr = mpdd->SetCooperativeLevel(NULL, DDSCL_NORMAL);
  195.         if (FAILED(hr)) {
  196.             DEBUG_LOG("VideoDriver/DDraw: Couldn't set cooperative level\n");
  197.             break;
  198.         }
  199.  
  200.         // attempt to create primary surface
  201.         if (!InitPrimary())
  202.             break;
  203.  
  204.         mInitCount = 1;
  205.         mClients.push_back(pClient);
  206.         return true;
  207.     } while(false);
  208.  
  209.     Shutdown(NULL);
  210.     return false;
  211. }
  212.  
  213. bool VDDirectDrawManager::InitPrimary() {
  214.     do {
  215.         // attempt to create primary surface
  216.         DDSURFACEDESC ddsdPri = {sizeof(DDSURFACEDESC)};
  217.         IDirectDrawSurface *pdds;
  218.  
  219.         ddsdPri.dwFlags                = DDSD_CAPS;
  220.         ddsdPri.ddsCaps.dwCaps        = DDSCAPS_PRIMARYSURFACE;
  221.         ddsdPri.ddpfPixelFormat.dwSize    = sizeof(DDPIXELFORMAT);
  222.  
  223.         if (FAILED(mpdd->CreateSurface(&ddsdPri, &pdds, NULL))) {
  224.             DEBUG_LOG("VideoDriver/DDraw: Couldn't create primary surface\n");
  225.             break;
  226.         }
  227.  
  228.         // query up to IDirectDrawSurface2 (DX3)
  229.         HRESULT hr = pdds->QueryInterface(IID_IDirectDrawSurface2, (void **)&mpddsPrimary);
  230.         pdds->Release();
  231.  
  232.         if (FAILED(hr))
  233.             break;
  234.  
  235.         // We cannot call GetSurfaceDesc() on the Primary as it causes the Vista beta 2
  236.         // DWM to freak out.
  237.         if (FAILED(mpdd->GetDisplayMode(&ddsdPri))) {
  238.             DEBUG_LOG("VideoDriver/DDraw: Couldn't get primary desc\n");
  239.             break;
  240.         }
  241.  
  242.         mPrimaryDesc = ddsdPri;
  243.  
  244.         return true;
  245.     } while(false);
  246.  
  247.     ShutdownPrimary();
  248.     return false;
  249. }
  250.  
  251. bool VDDirectDrawManager::Restore() {
  252.     if (mpddsPrimary) {
  253.         if (SUCCEEDED(mpddsPrimary->IsLost()))
  254.             return true;
  255.  
  256.         VDDEBUG_DISP("VDDirectDraw: Primary surface restore requested.\n");
  257.  
  258.         HRESULT hr = mpddsPrimary->Restore();
  259.  
  260.         if (FAILED(hr)) {
  261.             VDDEBUG_DISP("VDDirectDraw: Primary surface restore failed -- tearing down DirectDraw!\n");
  262.  
  263.             for(tClients::iterator it(mClients.begin()), itEnd(mClients.end()); it!=itEnd; ++it) {
  264.                 IVDDirectDrawClient *pClient = *it;
  265.  
  266.                 pClient->DirectDrawShutdown();
  267.             }
  268.  
  269.             if (!mInitCount) {
  270.                 VDDEBUG_DISP("VDDirectDraw: All clients vacated.\n");
  271.                 return false;
  272.             }
  273.  
  274.             Shutdown(NULL);
  275.             if (!Init(NULL)) {
  276.                 VDDEBUG_DISP("VDDirectDraw: Couldn't resurrect DirectDraw!\n");
  277.                 return false;
  278.             }
  279.         }
  280.     } else {
  281.         if (!InitPrimary())
  282.             return false;
  283.     }
  284.  
  285.     VDDEBUG_DISP("VDDirectDraw: Primary surface restore complete.\n");
  286.     for(tClients::iterator it(mClients.begin()), itEnd(mClients.end()); it!=itEnd; ++it) {
  287.         IVDDirectDrawClient *pClient = *it;
  288.  
  289.         pClient->DirectDrawPrimaryRestored();
  290.     }
  291.  
  292.     return true;
  293. }
  294.  
  295. void VDDirectDrawManager::ShutdownPrimary() {
  296.     if (mpddsPrimary) {
  297.         mpddsPrimary->Release();
  298.         mpddsPrimary = 0;
  299.     }
  300. }
  301.  
  302. void VDDirectDrawManager::Shutdown(IVDDirectDrawClient *pClient) {
  303.     if (pClient) {
  304.         tClients::iterator it(std::find(mClients.begin(), mClients.end(), pClient));
  305.  
  306.         if (it != mClients.end()) {
  307.             *it = mClients.back();
  308.             mClients.pop_back();
  309.         }
  310.  
  311.         if (--mInitCount)
  312.             return;
  313.     }
  314.  
  315.     ShutdownPrimary();
  316.  
  317.     if (mpdd) {
  318.         mpdd->Release();
  319.         mpdd = 0;
  320.     }
  321.  
  322.     if (mhmodDD) {
  323.         FreeLibrary(mhmodDD);
  324.         mhmodDD = 0;
  325.     }    
  326. }
  327.  
  328. VDDirectDrawManager g_ddman;
  329.  
  330. IVDDirectDrawManager *VDInitDirectDraw(IVDDirectDrawClient *pClient) {
  331.     VDASSERT(pClient);
  332.     return g_ddman.Init(pClient) ? &g_ddman : NULL;
  333. }
  334.  
  335. void VDShutdownDirectDraw(IVDDirectDrawClient *pClient) {
  336.     VDASSERT(pClient);
  337.     g_ddman.Shutdown(pClient);
  338. }
  339.  
  340. ///////////////////////////////////////////////////////////////////////////
  341.  
  342. class VDVideoDisplayMinidriverDirectDraw : public VDVideoDisplayMinidriver, protected IVDDirectDrawClient {
  343. public:
  344.     VDVideoDisplayMinidriverDirectDraw(bool enableOverlays, bool enableOldSecondaryMonitorBehavior);
  345.     ~VDVideoDisplayMinidriverDirectDraw();
  346.  
  347.     bool Init(HWND hwnd, const VDVideoDisplaySourceInfo& info);
  348.     void Shutdown();
  349.  
  350.     bool ModifySource(const VDVideoDisplaySourceInfo& info);
  351.  
  352.     bool IsValid();
  353.     bool IsFramePending() { return mbPresentPending; }
  354.  
  355.     bool Tick(int id);
  356.     void Poll();
  357.     bool Resize();
  358.     bool Update(UpdateMode);
  359.     void Refresh(UpdateMode);
  360.     bool Paint(HDC hdc, const RECT& rClient, UpdateMode mode);
  361.     bool SetSubrect(const vdrect32 *r);
  362.     void SetLogicalPalette(const uint8 *pLogicalPalette) { mpLogicalPalette = pLogicalPalette; }
  363.  
  364. protected:
  365.     enum {
  366.         kOverlayUpdateTimerId = 200
  367.     };
  368.  
  369.     void DirectDrawShutdown() {
  370.         Shutdown();
  371.         mbReset = true;
  372.     }
  373.  
  374.     void DirectDrawPrimaryRestored() {
  375.         memset(&mLastDisplayRect, 0, sizeof mLastDisplayRect);
  376.     }
  377.  
  378.     bool InitOverlay();
  379.     bool InitOffscreen();
  380.     void ShutdownDisplay();
  381.     bool UpdateOverlay(bool force);
  382.     bool InternalRefresh(const RECT& rClient, UpdateMode mode, bool newFrame, bool doNotWait);
  383.     bool InternalBlt(IDirectDrawSurface2 *&pDest, RECT *prDst, RECT *prSrc, bool doNotWait, bool& stillDrawing);
  384.  
  385.     HWND        mhwnd;
  386.     IVDDirectDrawManager    *mpddman;
  387.     IDirectDrawClipper    *mpddc;
  388.     IDirectDrawSurface2    *mpddsBitmap;
  389.     IDirectDrawSurface2    *mpddsOverlay;
  390.     int            mPrimaryFormat;
  391.     int            mPrimaryW;
  392.     int            mPrimaryH;
  393.     const uint8 *mpLogicalPalette;
  394.  
  395.     RECT        mrClient;
  396.     RECT        mLastDisplayRect;
  397.     UINT        mOverlayUpdateTimer;
  398.  
  399.     COLORREF    mChromaKey;
  400.     unsigned    mRawChromaKey;
  401.  
  402.     bool        mbReset;
  403.     bool        mbValid;
  404.     bool        mbFirstFrame;
  405.     bool        mbRepaintOnNextUpdate;
  406.     bool        mbPresentPending;
  407.     bool        mbSwapChromaPlanes;
  408.     bool        mbUseSubrect;
  409.     uint32        mPresentPendingFlags;
  410.     vdrect32    mSubrect;
  411.  
  412.     bool        mbEnableOverlays;
  413.     bool        mbEnableSecondaryDraw;
  414.  
  415.     DDCAPS        mCaps;
  416.     VDVideoDisplaySourceInfo    mSource;
  417.  
  418.     VDDDrawPresentHistory    mPresentHistory;
  419. };
  420.  
  421. IVDVideoDisplayMinidriver *VDCreateVideoDisplayMinidriverDirectDraw(bool enableOverlays, bool enableOldSecondaryMonitorBehavior) {
  422.     return new VDVideoDisplayMinidriverDirectDraw(enableOverlays, enableOldSecondaryMonitorBehavior);
  423. }
  424.  
  425. VDVideoDisplayMinidriverDirectDraw::VDVideoDisplayMinidriverDirectDraw(bool enableOverlays, bool enableOldSecondaryMonitorBehavior)
  426.     : mhwnd(0)
  427.     , mpddman(0)
  428.     , mpddc(0)
  429.     , mpddsBitmap(0)
  430.     , mpddsOverlay(0)
  431.     , mpLogicalPalette(NULL)
  432.     , mOverlayUpdateTimer(0)
  433.     , mbReset(false)
  434.     , mbValid(false)
  435.     , mbFirstFrame(false)
  436.     , mbPresentPending(false)
  437.     , mbRepaintOnNextUpdate(false)
  438.     , mbUseSubrect(false)
  439.     , mPresentPendingFlags(0)
  440.     , mbEnableOverlays(enableOverlays)
  441.     , mbEnableSecondaryDraw(enableOldSecondaryMonitorBehavior)
  442. {
  443.     memset(&mSource, 0, sizeof mSource);
  444.     mrClient.top = mrClient.left = mrClient.right = mrClient.bottom = 0;
  445. }
  446.  
  447. VDVideoDisplayMinidriverDirectDraw::~VDVideoDisplayMinidriverDirectDraw() {
  448. }
  449.  
  450. bool VDVideoDisplayMinidriverDirectDraw::Init(HWND hwnd, const VDVideoDisplaySourceInfo& info) {
  451.     switch(info.pixmap.format) {
  452.     case nsVDPixmap::kPixFormat_Pal8:
  453.     case nsVDPixmap::kPixFormat_XRGB1555:
  454.     case nsVDPixmap::kPixFormat_RGB565:
  455.     case nsVDPixmap::kPixFormat_RGB888:
  456.     case nsVDPixmap::kPixFormat_XRGB8888:
  457.     case nsVDPixmap::kPixFormat_YUV422_YUYV:
  458.     case nsVDPixmap::kPixFormat_YUV422_UYVY:
  459.     case nsVDPixmap::kPixFormat_YUV444_Planar:
  460.     case nsVDPixmap::kPixFormat_YUV422_Planar:
  461.     case nsVDPixmap::kPixFormat_YUV420_Planar:
  462.     case nsVDPixmap::kPixFormat_YUV411_Planar:
  463.     case nsVDPixmap::kPixFormat_YUV410_Planar:
  464.     case nsVDPixmap::kPixFormat_Y8:
  465.     case nsVDPixmap::kPixFormat_YUV422_V210:
  466.     case nsVDPixmap::kPixFormat_YUV422_UYVY_709:
  467.     case nsVDPixmap::kPixFormat_YUV420_NV12:
  468.         break;
  469.     default:
  470.         return false;
  471.     }
  472.  
  473.     mhwnd    = hwnd;
  474.     mSource    = info;
  475.     GetClientRect(hwnd, &mrClient);
  476.  
  477.     do {
  478.         mpddman = VDInitDirectDraw(this);
  479.         if (!mpddman)
  480.             break;
  481.  
  482.         // The Windows Vista DWM has a bug where it allows you to create an overlay surface even
  483.         // though you'd never be able to display it -- so we have to detect the DWM and force
  484.         // overlays off.
  485.         bool allowOverlay = mbEnableOverlays && !mbUseSubrect;
  486.  
  487.         if (mbEnableOverlays) {
  488.             // Looks like some systems have screwed up configs where either someone has inserted
  489.             // a fake DWMAPI.DLL into the path or have somehow gotten it installed on an XP system;
  490.             // the result is a failed dependency error when we try loading it. We avoid this by
  491.             // explicitly checking for Windows Vista or higher.
  492.  
  493.             OSVERSIONINFO osInfo = { sizeof(OSVERSIONINFO) };
  494.             if (GetVersionEx(&osInfo) && osInfo.dwMajorVersion >= 6) {
  495.                 HMODULE hmodDwmApi = LoadLibraryA("dwmapi");
  496.                 if (hmodDwmApi) {
  497.                     typedef HRESULT (WINAPI *tpDwmIsCompositionEnabled)(BOOL *);
  498.  
  499.                     tpDwmIsCompositionEnabled pDwmIsCompositionEnabled = (tpDwmIsCompositionEnabled)GetProcAddress(hmodDwmApi, "DwmIsCompositionEnabled");
  500.                     if (pDwmIsCompositionEnabled) {
  501.                         BOOL enabled;
  502.                         HRESULT hr = pDwmIsCompositionEnabled(&enabled);
  503.  
  504.                         if (SUCCEEDED(hr) && enabled)
  505.                             allowOverlay = false;
  506.                     }
  507.  
  508.                     FreeLibrary(hmodDwmApi);
  509.                 }
  510.             }
  511.         }
  512.  
  513.         mCaps = mpddman->GetCaps();
  514.  
  515.         const DDSURFACEDESC& ddsdPri = mpddman->GetPrimaryDesc();
  516.  
  517.         mPrimaryW = ddsdPri.dwWidth;
  518.         mPrimaryH = ddsdPri.dwHeight;
  519.  
  520.         // Interestingly enough, if another app goes full-screen, it's possible for us to lose
  521.         // the device and have a failed Restore() between InitOverlay() and InitOffscreen().
  522.         if ((allowOverlay && InitOverlay()) || (mpddman && InitOffscreen()))
  523.             return true;
  524.  
  525.     } while(false);
  526.  
  527.     Shutdown();
  528.     return false;
  529. }
  530.  
  531. bool VDVideoDisplayMinidriverDirectDraw::InitOverlay() {
  532.     DWORD dwFourCC;
  533.     int minw = 1;
  534.     int minh = 1;
  535.  
  536.     mbSwapChromaPlanes = false;
  537.     switch(mSource.pixmap.format) {
  538.     case nsVDPixmap::kPixFormat_YUV422_YUYV:
  539.         dwFourCC = MAKEFOURCC('Y', 'U', 'Y', '2');
  540.         minw = 2;
  541.         break;
  542.  
  543.     case nsVDPixmap::kPixFormat_YUV422_UYVY:
  544.         dwFourCC = MAKEFOURCC('U', 'Y', 'V', 'Y');
  545.         minw = 2;
  546.         break;
  547.  
  548.     case nsVDPixmap::kPixFormat_YUV420_Planar:
  549.         dwFourCC = MAKEFOURCC('Y', 'V', '1', '2');
  550.         mbSwapChromaPlanes = true;
  551.         minw = 2;
  552.         minh = 2;
  553.         break;
  554.  
  555.     case nsVDPixmap::kPixFormat_YUV422_Planar:
  556.         dwFourCC = MAKEFOURCC('Y', 'V', '1', '6');
  557.         mbSwapChromaPlanes = true;
  558.         minw = 2;
  559.         break;
  560.  
  561.     case nsVDPixmap::kPixFormat_YUV410_Planar:
  562.         dwFourCC = MAKEFOURCC('Y', 'V', 'U', '9');
  563.         mbSwapChromaPlanes = true;
  564.         minw = 4;
  565.         minh = 4;
  566.         break;
  567.  
  568.     case nsVDPixmap::kPixFormat_Y8:
  569.         dwFourCC = MAKEFOURCC('Y', '8', ' ', ' ');
  570.         mbSwapChromaPlanes = true;
  571.         break;
  572.  
  573.     // Disabled because ForceWare 175.16 on XP+Quadro NVS 140M doesn't flip properly with
  574.     // NV12 overlays.
  575.     #if 0
  576.         case nsVDPixmap::kPixFormat_YUV420_NV12:
  577.             dwFourCC = MAKEFOURCC('N', 'V', '1', '2');
  578.             minw = 2;
  579.             minh = 2;
  580.             break;
  581.     #endif
  582.  
  583.     default:
  584.         return false;
  585.     }
  586.  
  587.     do {
  588.         // create overlay surface
  589.         DDSURFACEDESC ddsdOff = {sizeof(DDSURFACEDESC)};
  590.  
  591.         ddsdOff.dwFlags                        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
  592.         ddsdOff.dwWidth                        = (mSource.pixmap.w + minw - 1) & -minw;
  593.         ddsdOff.dwHeight                    = (mSource.pixmap.h + minh - 1) & -minh;
  594.         ddsdOff.ddsCaps.dwCaps                = DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
  595.         ddsdOff.ddpfPixelFormat.dwSize        = sizeof(DDPIXELFORMAT);
  596.         ddsdOff.ddpfPixelFormat.dwFlags        = DDPF_FOURCC;
  597.         ddsdOff.ddpfPixelFormat.dwFourCC    = dwFourCC;
  598.  
  599.         if (mCaps.dwCaps & DDCAPS_ALIGNSIZESRC) {
  600.             ddsdOff.dwWidth += mCaps.dwAlignSizeSrc - 1;
  601.             ddsdOff.dwWidth -= ddsdOff.dwWidth % mCaps.dwAlignSizeSrc;
  602.         }
  603.  
  604.         IDirectDrawSurface *pdds;
  605.         HRESULT hr = mpddman->GetDDraw()->CreateSurface(&ddsdOff, &pdds, NULL);
  606.  
  607.         if (FAILED(hr)) {
  608.             DEBUG_LOG("VideoDisplay/DDraw: Overlay surface creation failed\n");
  609.             break;
  610.         }
  611.  
  612.         hr = pdds->QueryInterface(IID_IDirectDrawSurface2, (void **)&mpddsOverlay);
  613.         pdds->Release();
  614.  
  615.         if (FAILED(hr))
  616.             break;
  617.  
  618.         // Do not allow colorkey if the primary surface is paletted, as we may not be able
  619.         // to reliably choose the correct color.
  620.         mChromaKey = 0;
  621.  
  622.         if (!(mpddman->GetPrimaryDesc().ddpfPixelFormat.dwFlags & (DDPF_PALETTEINDEXED8|DDPF_PALETTEINDEXED4))) {
  623.             if (mCaps.dwCKeyCaps & DDCKEYCAPS_DESTOVERLAY) {
  624.                 const DDSURFACEDESC& ddsdPri = mpddman->GetPrimaryDesc();
  625.  
  626.                 mRawChromaKey = ddsdPri.ddpfPixelFormat.dwGBitMask & ~(ddsdPri.ddpfPixelFormat.dwGBitMask >> 1);
  627.                 mChromaKey = RGB(0,128,0);
  628.             }
  629.         }
  630.  
  631.         mOverlayUpdateTimer = SetTimer(mhwnd, kOverlayUpdateTimerId, 100, NULL);
  632.         memset(&mLastDisplayRect, 0, sizeof mLastDisplayRect);
  633.  
  634.         VDDEBUG_DISP("VideoDisplay: Using DirectDraw overlay for %dx%d %s display.\n", mSource.pixmap.w, mSource.pixmap.h, VDPixmapGetInfo(mSource.pixmap.format).name);
  635.         DEBUG_LOG("VideoDisplay/DDraw: Overlay surface creation successful\n");
  636.  
  637.         mbRepaintOnNextUpdate = true;
  638.         mbValid = false;
  639.  
  640.         if (!UpdateOverlay(false))
  641.             break;
  642.  
  643.         return true;
  644.     } while(false);
  645.  
  646.     ShutdownDisplay();
  647.     return false;
  648. }
  649.  
  650. bool VDVideoDisplayMinidriverDirectDraw::InitOffscreen() {
  651.     HRESULT hr;
  652.  
  653.     do {
  654.         const DDPIXELFORMAT& pf = mpddman->GetPrimaryDesc().ddpfPixelFormat;
  655.  
  656.         // determine primary surface pixel format
  657.         if (pf.dwFlags & DDPF_PALETTEINDEXED8) {
  658.             mPrimaryFormat = nsVDPixmap::kPixFormat_Pal8;
  659.             VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is 8-bit paletted.\n");
  660.         } else if (pf.dwFlags & DDPF_RGB) {
  661.             if (   pf.dwRGBBitCount == 16 && pf.dwRBitMask == 0x7c00 && pf.dwGBitMask == 0x03e0 && pf.dwBBitMask == 0x001f) {
  662.                 mPrimaryFormat = nsVDPixmap::kPixFormat_XRGB1555;
  663.                 VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is 16-bit xRGB (1-5-5-5).\n");
  664.             } else if (pf.dwRGBBitCount == 16 && pf.dwRBitMask == 0xf800 && pf.dwGBitMask == 0x07e0 && pf.dwBBitMask == 0x001f) {
  665.                 mPrimaryFormat = nsVDPixmap::kPixFormat_RGB565;
  666.                 VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is 16-bit RGB (5-6-5).\n");
  667.             } else if (pf.dwRGBBitCount == 24 && pf.dwRBitMask == 0xff0000 && pf.dwGBitMask == 0x00ff00 && pf.dwBBitMask == 0x0000ff) {
  668.                 mPrimaryFormat = nsVDPixmap::kPixFormat_RGB888;
  669.                 VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is 24-bit RGB (8-8-8).\n");
  670.             } else if (pf.dwRGBBitCount == 32 && pf.dwRBitMask == 0xff0000 && pf.dwGBitMask == 0x00ff00 && pf.dwBBitMask == 0x0000ff) {
  671.                 mPrimaryFormat = nsVDPixmap::kPixFormat_XRGB8888;
  672.                 VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is 32-bit xRGB (8-8-8-8).\n");
  673.             } else
  674.                 break;
  675.         } else
  676.             break;
  677.  
  678.         if (mPrimaryFormat != mSource.pixmap.format) {
  679.             if (!mSource.bAllowConversion) {
  680.                 VDDEBUG_DISP("VideoDisplay/DirectDraw: Display is not compatible with source and conversion is disallowed.\n");
  681.                 return false;
  682.             }
  683.         }
  684.  
  685.         // attempt to create clipper
  686.         if (FAILED(mpddman->GetDDraw()->CreateClipper(0, &mpddc, 0)))
  687.             break;
  688.  
  689.         if (FAILED(mpddc->SetHWnd(0, mhwnd)))
  690.             break;
  691.  
  692.         // create bitmap surface
  693.         DDSURFACEDESC ddsdOff = {sizeof(DDSURFACEDESC)};
  694.  
  695.         ddsdOff.dwFlags                    = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
  696.         ddsdOff.dwWidth                    = mSource.pixmap.w;
  697.         ddsdOff.dwHeight                = mSource.pixmap.h;
  698.         ddsdOff.ddsCaps.dwCaps            = DDSCAPS_OFFSCREENPLAIN;
  699.         ddsdOff.ddpfPixelFormat            = pf;
  700.  
  701.         IDirectDrawSurface *pdds = NULL;
  702.  
  703.         // if the source is persistent, try to create the surface directly into system memory
  704.         if (mSource.bPersistent) {
  705.             mSource.bPersistent = false;
  706.  
  707. #if 0        // doesn't work in DX3 -- need DX7 interfaces to create client surfaces
  708.             if (mPrimaryFormat == mSource.format) {
  709.                 DDSURFACEDESC ddsdOff2(ddsdOff);
  710.  
  711.                 ddsdOff2.dwFlags            = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_PITCH | DDSD_LPSURFACE;
  712.                 ddsdOff2.lpSurface            = (void *)mSource.data;
  713.                 ddsdOff2.lPitch                = mSource.pitch;
  714.                 ddsdOff2.ddsCaps.dwCaps        = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
  715.                 if (SUCCEEDED(mpddman->GetDDraw()->CreateSurface(&ddsdOff2, &pdds, NULL))) {
  716.                     DEBUG_LOG("VideoDriver/DDraw: Created surface directly in system memory (lucky!)\n");
  717.                     mSource.bPersistent = true;
  718.                 }
  719.             }
  720. #endif
  721.         }
  722.  
  723.         if (!pdds && FAILED(mpddman->GetDDraw()->CreateSurface(&ddsdOff, &pdds, NULL))) {
  724.             DEBUG_LOG("VideoDriver/DDraw: Couldn't create offscreen surface\n");
  725.             break;
  726.         }
  727.  
  728.         hr = pdds->QueryInterface(IID_IDirectDrawSurface2, (void **)&mpddsBitmap);
  729.         pdds->Release();
  730.  
  731.         if (FAILED(hr))
  732.             break;
  733.  
  734.         mChromaKey = 0;
  735.         mbValid = false;
  736.         mbRepaintOnNextUpdate = false;
  737.         mbFirstFrame = true;
  738.  
  739.         DEBUG_LOG("VideoDriver/DDraw: Offscreen initialization successful\n");
  740.         VDDEBUG_DISP("VideoDisplay: Using DirectDraw offscreen surface for %dx%d %s display.\n", mSource.pixmap.w, mSource.pixmap.h, VDPixmapGetInfo(mSource.pixmap.format).name);
  741.         return true;
  742.     } while(false); 
  743.  
  744.     ShutdownDisplay();
  745.     return false;
  746. }
  747.  
  748. void VDVideoDisplayMinidriverDirectDraw::ShutdownDisplay() {
  749.     if (mpddc) {
  750.         mpddc->Release();
  751.         mpddc = 0;
  752.     }
  753.  
  754.     if (mpddsBitmap) {
  755.         mpddsBitmap->Release();
  756.         mpddsBitmap = 0;
  757.     }
  758.  
  759.     if (mpddsOverlay) {
  760.         mpddsOverlay->Release();
  761.         mpddsOverlay = 0;
  762.     }
  763.  
  764.     mbValid = false;
  765. }
  766.  
  767. void VDVideoDisplayMinidriverDirectDraw::Shutdown() {
  768.     ShutdownDisplay();
  769.     
  770.     if (mpddman) {
  771.         VDShutdownDirectDraw(this);
  772.         mpddman = NULL;
  773.     }
  774. }
  775.  
  776. bool VDVideoDisplayMinidriverDirectDraw::ModifySource(const VDVideoDisplaySourceInfo& info) {
  777.     if (!mpddsBitmap && !mpddsOverlay)
  778.         return false;
  779.  
  780.     if (mSource.pixmap.w == info.pixmap.w && mSource.pixmap.h == info.pixmap.h && mSource.pixmap.format == info.pixmap.format) {
  781.         mSource = info;
  782.         return true;
  783.     }
  784.  
  785.     return false;
  786. }
  787.  
  788. bool VDVideoDisplayMinidriverDirectDraw::IsValid() {
  789.     return mbValid && ((mpddsOverlay && DD_OK == mpddsOverlay->IsLost()) || (mpddsBitmap && DD_OK == mpddsBitmap->IsLost()));
  790. }
  791.  
  792. bool VDVideoDisplayMinidriverDirectDraw::Tick(int id) {
  793.     if (id == kOverlayUpdateTimerId) {
  794.         RECT r;
  795.         GetClientRect(mhwnd, &r);
  796.         MapWindowPoints(mhwnd, NULL, (LPPOINT)&r, 2);
  797.  
  798.         if (memcmp(&r, &mLastDisplayRect, sizeof(RECT)))
  799.             Resize();
  800.     }
  801.  
  802.     return !mbReset;
  803. }
  804.  
  805. void VDVideoDisplayMinidriverDirectDraw::Poll() {
  806.     if (mbPresentPending)
  807.         InternalRefresh(mrClient, (UpdateMode)mPresentPendingFlags, false, true);
  808. }
  809.  
  810. bool VDVideoDisplayMinidriverDirectDraw::Resize() {
  811.     GetClientRect(mhwnd, &mrClient);
  812.  
  813.     if (mpddsOverlay)
  814.         UpdateOverlay(false);
  815.  
  816.     return !mbReset;
  817. }
  818.  
  819. bool VDVideoDisplayMinidriverDirectDraw::UpdateOverlay(bool force) {
  820.     do {
  821.         RECT rDst0;
  822.  
  823.         GetClientRect(mhwnd, &rDst0);
  824.         MapWindowPoints(mhwnd, NULL, (LPPOINT)&rDst0, 2);
  825.  
  826.         // destination clipping
  827.         RECT rDst = rDst0;
  828.         const int dstw = rDst.right - rDst.left;
  829.         const int dsth = rDst.bottom - rDst.top;
  830.  
  831.         if (rDst.left < 0)
  832.             rDst.left = 0;
  833.  
  834.         if (rDst.top < 0)
  835.             rDst.top = 0;
  836.  
  837.         if (rDst.right > mPrimaryW)
  838.             rDst.right = mPrimaryW;
  839.  
  840.         if (rDst.bottom > mPrimaryH)
  841.             rDst.bottom = mPrimaryH;
  842.  
  843.         if (rDst.bottom <= rDst.top || rDst.right <= rDst.left)
  844.             break;
  845.  
  846.         // source clipping
  847.         RECT rSrc = {
  848.             (rDst.left   - rDst0.left) * mSource.pixmap.w / dstw,
  849.             (rDst.top    - rDst0.top ) * mSource.pixmap.h / dsth,
  850.             (rDst.right  - rDst0.left) * mSource.pixmap.w / dstw,
  851.             (rDst.bottom - rDst0.top ) * mSource.pixmap.h / dsth,
  852.         };
  853.  
  854.         // source alignment
  855.         if (mCaps.dwCaps & DDCAPS_ALIGNBOUNDARYSRC) {
  856.             int align = mCaps.dwAlignBoundarySrc;
  857.             rSrc.left -= rSrc.left % align;
  858.         }
  859.  
  860.         if (mCaps.dwCaps & DDCAPS_ALIGNSIZESRC) {
  861.             int w = rSrc.right - rSrc.left;
  862.  
  863.             w -= w % mCaps.dwAlignSizeSrc;
  864.  
  865.             rSrc.right = rSrc.left + w;
  866.         }
  867.  
  868.         // destination alignment
  869.         if (mCaps.dwCaps & DDCAPS_ALIGNBOUNDARYDEST) {
  870.             int align = mCaps.dwAlignBoundaryDest;
  871.  
  872.             rDst.left += align-1;
  873.             rDst.left -= rDst.left % align;
  874.         }
  875.  
  876.         if (mCaps.dwCaps & DDCAPS_ALIGNSIZEDEST) {
  877.             int w = rDst.right - rDst.left;
  878.  
  879.             w -= w % mCaps.dwAlignSizeDest;
  880.  
  881.             if (w <= 0)
  882.                 break;
  883.  
  884.             rDst.right = rDst.left + w;
  885.         }
  886.  
  887.         DWORD dwFlags = DDOVER_SHOW | DDOVER_DDFX;
  888.         DDOVERLAYFX ddfx = {sizeof(DDOVERLAYFX)};
  889.  
  890.         if (mChromaKey) {
  891.             dwFlags |= DDOVER_KEYDESTOVERRIDE;
  892.             ddfx.dckDestColorkey.dwColorSpaceLowValue = mRawChromaKey;
  893.             ddfx.dckDestColorkey.dwColorSpaceHighValue = mRawChromaKey;
  894.         }
  895.  
  896.         if (mCaps.dwFXCaps & DDFXCAPS_OVERLAYARITHSTRETCHY)
  897.             ddfx.dwFlags |= DDOVERFX_ARITHSTRETCHY;
  898.  
  899.         IDirectDrawSurface2 *pDest = mpddman->GetPrimary();
  900.         HRESULT hr = mpddsOverlay->UpdateOverlay(&rSrc, pDest, &rDst, dwFlags, &ddfx);
  901.  
  902.         if (FAILED(hr)) {
  903.             mbValid = false;
  904.             memset(&mLastDisplayRect, 0, sizeof mLastDisplayRect);
  905.  
  906.             // NVIDIA ForceWare 96.85 for Vista allows us to create multiple overlays,
  907.             // but attempting to show more than one gives DDERR_NOTAVAILABLE.
  908.             if (hr != DDERR_SURFACELOST)
  909.                 return false;
  910.  
  911.             if (FAILED(mpddsOverlay->Restore()))
  912.                 return false;
  913.  
  914.             if (FAILED(pDest->IsLost()) && mpddman->Restore())
  915.                 return false;
  916.         } else
  917.             mLastDisplayRect = rDst0;
  918.         return !mbReset;
  919.     } while(false);
  920.  
  921.     mpddsOverlay->UpdateOverlay(NULL, mpddman->GetPrimary(), NULL, DDOVER_HIDE, NULL);
  922.     return !mbReset;
  923. }
  924.  
  925. bool VDVideoDisplayMinidriverDirectDraw::Update(UpdateMode mode) {
  926.     if (!mSource.pixmap.data)
  927.         return false;
  928.  
  929.     HRESULT hr;
  930.     DDSURFACEDESC ddsd = { sizeof(DDSURFACEDESC) };
  931.  
  932.     ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
  933.     
  934.     static DWORD dwLockFlags = GetVersion() & 0x80000000 ? DDLOCK_WRITEONLY | DDLOCK_NOSYSLOCK | DDLOCK_WAIT : DDLOCK_WRITEONLY | DDLOCK_WAIT;
  935.  
  936.     IDirectDrawSurface2 *pTarget = mpddsBitmap ? mpddsBitmap : mpddsOverlay;
  937.  
  938.     if (!pTarget)
  939.         return false;
  940.  
  941.     // When NView reverts between dual-display modes, we can get a DDERR_SURFACELOST on which
  942.     // Restore() succeeds, but the next lock still fails. We insert a safety counter here to
  943.     // prevent a hang.
  944.     for(int retries=0; retries<5; ++retries) {
  945.         hr = pTarget->Lock(NULL, &ddsd, dwLockFlags, 0);
  946.  
  947.         if (SUCCEEDED(hr))
  948.             break;
  949.  
  950.         if (hr != DDERR_SURFACELOST)
  951.             break;
  952.  
  953.         mbValid = false;
  954.         memset(&mLastDisplayRect, 0, sizeof mLastDisplayRect);
  955.  
  956.         if (!mpddman->Restore())
  957.             break;
  958.  
  959.         hr = pTarget->Restore();
  960.         if (FAILED(hr))
  961.             break;
  962.     }
  963.  
  964.     if (FAILED(hr)) {
  965.         mbValid = false;
  966.         memset(&mLastDisplayRect, 0, sizeof mLastDisplayRect);
  967.         return false;
  968.     }
  969.  
  970.     VDPixmap source(mSource.pixmap);
  971.  
  972.     char *dst = (char *)ddsd.lpSurface;
  973.     ptrdiff_t dstpitch = ddsd.lPitch;
  974.  
  975.     uint32 fieldmode = mode & kModeFieldMask;
  976.  
  977.     VDPixmap dstbm = { dst, NULL, ddsd.dwWidth, ddsd.dwHeight, dstpitch, mPrimaryFormat };
  978.  
  979.     if (mpddsOverlay)
  980.         dstbm.format = source.format;
  981.  
  982.     const VDPixmapFormatInfo& dstinfo = VDPixmapGetInfo(dstbm.format);
  983.  
  984.     if (dstinfo.auxbufs >= 1) {
  985.         const int qw = -(-dstbm.w >> dstinfo.qwbits);
  986.         const int qh = -(-dstbm.h >> dstinfo.qhbits);
  987.  
  988.         VDASSERT((qw << dstinfo.qwbits) == dstbm.w);
  989.         VDASSERT((qh << dstinfo.qhbits) == dstbm.h);
  990.  
  991.         dstbm.data2        = (char *)dstbm.data + dstpitch * qh;
  992.         dstbm.pitch2    = dstpitch >> dstinfo.auxwbits;
  993.  
  994.         if (dstinfo.auxbufs >= 2) {
  995.             dstbm.data3 = (char *)dstbm.data2 + dstbm.pitch2 * -(-dstbm.h >> dstinfo.auxhbits);
  996.             dstbm.pitch3 = dstbm.pitch2;
  997.         }
  998.  
  999.         if (mbSwapChromaPlanes) {
  1000.             std::swap(dstbm.data2, dstbm.data3);
  1001.             std::swap(dstbm.pitch2, dstbm.pitch3);
  1002.         }
  1003.     }
  1004.  
  1005.     if (mSource.bInterlaced && fieldmode != kModeAllFields) {
  1006.         const VDPixmapFormatInfo& srcformat = VDPixmapGetInfo(source.format);
  1007.         const VDPixmapFormatInfo& dstformat = VDPixmapGetInfo(dstbm.format);
  1008.  
  1009.         if (!srcformat.qhbits && !dstformat.qhbits && !srcformat.auxhbits && !dstformat.auxhbits) {
  1010.             if (fieldmode == kModeOddField) {
  1011.                 source.h >>= 1;
  1012.  
  1013.                 vdptrstep(source.data, source.pitch);
  1014.                 switch(srcformat.auxbufs) {
  1015.                     case 2:    vdptrstep(source.data3, source.pitch3);
  1016.                     case 1:    vdptrstep(source.data2, source.pitch2);
  1017.                 }
  1018.  
  1019.                 dstbm.h >>= 1;
  1020.                 vdptrstep(dstbm.data, dstbm.pitch);
  1021.                 switch(dstformat.auxbufs) {
  1022.                     case 2:    vdptrstep(dstbm.data3, dstbm.pitch3);
  1023.                     case 1:    vdptrstep(dstbm.data2, dstbm.pitch2);
  1024.                 }
  1025.             } else {
  1026.                 source.h = (source.h + 1) >> 1;
  1027.                 dstbm.h = (dstbm.h + 1) >> 1;
  1028.             }
  1029.  
  1030.             source.pitch += source.pitch;
  1031.             switch(dstformat.auxbufs) {
  1032.                 case 2:    source.pitch3 += source.pitch3;
  1033.                 case 1:    source.pitch2 += source.pitch2;
  1034.             }
  1035.  
  1036.             dstbm.pitch += dstbm.pitch;
  1037.             switch(dstformat.auxbufs) {
  1038.                 case 2:    dstbm.pitch3 += dstbm.pitch3;
  1039.                 case 1:    dstbm.pitch2 += dstbm.pitch2;
  1040.             }
  1041.         }
  1042.     }
  1043.  
  1044.     bool dither = false;
  1045.     if (dstbm.format == nsVDPixmap::kPixFormat_Pal8 && dstbm.format != source.format) {
  1046.         switch(source.format) {
  1047.             case nsVDPixmap::kPixFormat_XRGB1555:
  1048.             case nsVDPixmap::kPixFormat_RGB565:
  1049.             case nsVDPixmap::kPixFormat_RGB888:
  1050.             case nsVDPixmap::kPixFormat_XRGB8888:
  1051.                 dither = true;
  1052.                 break;
  1053.         }
  1054.     }
  1055.  
  1056.     if (dither)
  1057.         VDDitherImage(dstbm, source, mpLogicalPalette);
  1058.     else
  1059.         VDPixmapBlt(dstbm, source);
  1060.     
  1061.     hr = pTarget->Unlock(0);
  1062.  
  1063.     mbValid = SUCCEEDED(hr);
  1064.  
  1065.     if (mbValid) {
  1066.         mbPresentPending = true;
  1067.         mPresentPendingFlags = mode;
  1068.     }
  1069.  
  1070.     return !mbReset;
  1071. }
  1072.  
  1073. void VDVideoDisplayMinidriverDirectDraw::Refresh(UpdateMode mode) {
  1074.     if (mbValid) {
  1075.         if (mpddsOverlay) {
  1076.             Tick(kOverlayUpdateTimerId);
  1077.             if (mbRepaintOnNextUpdate) {
  1078.                 InvalidateRect(mhwnd, NULL, TRUE);
  1079.                 mbRepaintOnNextUpdate = false;
  1080.             }
  1081.  
  1082.             mbPresentPending = false;
  1083.             mSource.mpCB->RequestNextFrame();
  1084.         } else {
  1085.             RECT r;
  1086.             GetClientRect(mhwnd, &r);
  1087.             InternalRefresh(r, mode, true, (mode & kModeVSync) != 0);
  1088.         }
  1089.     }
  1090. }
  1091.  
  1092. bool VDVideoDisplayMinidriverDirectDraw::Paint(HDC hdc, const RECT& rClient, UpdateMode mode) {
  1093.     if (mpddsOverlay) {
  1094.         if (mChromaKey) {
  1095.             SetBkColor(hdc, mChromaKey);
  1096.             ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rClient, "", 0, NULL);
  1097.         }
  1098.     } else {
  1099.         InternalRefresh(rClient, mode, true, false);
  1100.     }
  1101.  
  1102.     // Workaround for Windows Vista DWM not adding window to composition tree immediately
  1103.     if (mbFirstFrame) {
  1104.         mbFirstFrame = false;
  1105.         SetWindowPos(mhwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
  1106.     }
  1107.  
  1108.     return !mbReset;
  1109. }
  1110.  
  1111. bool VDVideoDisplayMinidriverDirectDraw::SetSubrect(const vdrect32 *r) {
  1112.     if (mpddsOverlay)
  1113.         return false;
  1114.  
  1115.     if (r) {
  1116.         mbUseSubrect = true;
  1117.         mSubrect = *r;
  1118.     } else
  1119.         mbUseSubrect = false;
  1120.  
  1121.     return true;
  1122. }
  1123.  
  1124. bool VDVideoDisplayMinidriverDirectDraw::InternalRefresh(const RECT& rClient, UpdateMode mode, bool newFrame, bool doNotWait) {
  1125.     RECT rDst = rClient;
  1126.  
  1127.     // DirectX doesn't like null rects.
  1128.     if (rDst.right <= rDst.left || rDst.bottom <= rDst.top)
  1129.         return true;
  1130.  
  1131.     MapWindowPoints(mhwnd, NULL, (LPPOINT)&rDst, 2);
  1132.  
  1133.     IDirectDrawSurface2 *pDest = mpddman->GetPrimary();
  1134.  
  1135.     if (!pDest)
  1136.         return true;
  1137.  
  1138.     if (mColorOverride) {
  1139.         // convert color to primary surface format
  1140.         VDPixmap srcpx;
  1141.         srcpx.data = &mColorOverride;
  1142.         srcpx.pitch = 0;
  1143.         srcpx.w = 1;
  1144.         srcpx.h = 1;
  1145.         srcpx.format = nsVDPixmap::kPixFormat_XRGB8888;
  1146.  
  1147.         VDPixmap dstpx;
  1148.         uint32 tmpbuf;
  1149.         dstpx.data = &tmpbuf;
  1150.         dstpx.pitch = 0;
  1151.         dstpx.w = 1;
  1152.         dstpx.h = 1;
  1153.         dstpx.format = mPrimaryFormat;
  1154.  
  1155.         VDPixmapBlt(dstpx, srcpx);
  1156.  
  1157.         DDBLTFX fx = {sizeof(DDBLTFX)};
  1158.         fx.dwFillColor = tmpbuf;
  1159.  
  1160.         pDest->SetClipper(mpddc);
  1161.         for(int i=0; i<5; ++i) {
  1162.             HRESULT hr = pDest->Blt(&rDst, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &fx);
  1163.  
  1164.             if (SUCCEEDED(hr))
  1165.                 break;
  1166.  
  1167.             if (hr != DDERR_SURFACELOST)
  1168.                 break;
  1169.  
  1170.             if (FAILED(pDest->IsLost())) {
  1171.                 pDest->SetClipper(NULL);
  1172.                 pDest = NULL;
  1173.  
  1174.                 if (!mpddman->Restore())
  1175.                     return true;
  1176.  
  1177.                 if (mbReset)
  1178.                     return true;
  1179.  
  1180.                 pDest = mpddman->GetPrimary();
  1181.                 pDest->SetClipper(mpddc);
  1182.             }
  1183.         }
  1184.  
  1185.         pDest->SetClipper(NULL);
  1186.  
  1187.         // Workaround for Windows Vista DWM not adding window to composition tree immediately
  1188.         if (mbFirstFrame) {
  1189.             mbFirstFrame = false;
  1190.             SetWindowPos(mhwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
  1191.         }
  1192.         return true;
  1193.     }
  1194.  
  1195.     // DDBLTFX_NOTEARING is ignored by DirectDraw in 2K/XP.
  1196.     if (!(mode & kModeVSync)) {
  1197.         mPresentHistory.mbPresentPending = false;
  1198.     } else {
  1199.         IDirectDraw2 *pDD = mpddman->GetDDraw();
  1200.  
  1201.         if (newFrame && !mPresentHistory.mbPresentPending) {
  1202.             const vdrect32& monitorRect = mpddman->GetMonitorRect();
  1203.             int top = monitorRect.top;
  1204.             int bottom = monitorRect.bottom;
  1205.  
  1206.             RECT r(rDst);
  1207.             if (r.top < top)
  1208.                 r.top = top;
  1209.             if (r.bottom > bottom)
  1210.                 r.bottom = bottom;
  1211.  
  1212.             r.top -= top;
  1213.             r.bottom -= top;
  1214.  
  1215.             mPresentHistory.mScanTop = r.top;
  1216.             mPresentHistory.mScanBottom = r.bottom;
  1217.  
  1218.             mPresentHistory.mbPresentPending = true;
  1219.             mPresentHistory.mbPresentBlitStarted = false;
  1220.  
  1221.             mPresentHistory.mLastScanline = -1;
  1222.             mPresentHistory.mPresentStartTime = VDGetPreciseTick();
  1223.         }
  1224.  
  1225.         if (!mPresentHistory.mbPresentPending)
  1226.             return S_OK;
  1227.  
  1228.         // Poll raster status, and wait until we can safely blit. We assume that the
  1229.         // blit can outrace the beam. 
  1230.         ++mPresentHistory.mPollCount;
  1231.         for(;;) {
  1232.             // if we've already started the blit, skip beam-following
  1233.             if (mPresentHistory.mbPresentBlitStarted)
  1234.                 break;
  1235.  
  1236.             DWORD scan;
  1237.             bool inVBlank = false;
  1238.             HRESULT hr = pDD->GetScanLine(&scan);
  1239.             if (FAILED(hr)) {
  1240.                 scan = 0;
  1241.                 inVBlank = true;
  1242.             }
  1243.  
  1244.             sint32 y1 = (sint32)mPresentHistory.mLastScanline;
  1245.             if (y1 < 0) {
  1246.                 y1 = scan;
  1247.                 mPresentHistory.mAverageStartScanline += ((float)y1 - mPresentHistory.mAverageStartScanline) * 0.01f;
  1248.             }
  1249.  
  1250.             sint32 y2 = (sint32)scan;
  1251.  
  1252.             mPresentHistory.mbLastWasVBlank    = inVBlank ? true : false;
  1253.             mPresentHistory.mLastScanline    = scan;
  1254.  
  1255.             sint32 yt = (sint32)mPresentHistory.mScanlineTarget;
  1256.  
  1257.             mPresentHistory.mLastBracketY1 = y1;
  1258.             mPresentHistory.mLastBracketY2 = y2;
  1259.  
  1260.             // check for yt in [y1, y2]... but we have to watch for a beam wrap (y1 > y2).
  1261.             if (y1 <= y2) {
  1262.                 // non-wrap case
  1263.                 if (y1 <= yt && yt <= y2)
  1264.                     break;
  1265.             } else {
  1266.                 // wrap case
  1267.                 if (y1 <= yt || yt <= y2)
  1268.                     break;
  1269.             }
  1270.  
  1271.             if (doNotWait)
  1272.                 return false;
  1273.  
  1274.             ::Sleep(1);
  1275.         }
  1276.  
  1277.         mPresentHistory.mbPresentBlitStarted = true;
  1278.     }
  1279.  
  1280.     pDest->SetClipper(mpddc);
  1281.  
  1282.     bool success = true;
  1283.     bool stillDrawing = false;
  1284.     if (!mSource.bInterlaced) {
  1285.         if (mbUseSubrect) {
  1286.             RECT rSrc = { mSubrect.left, mSubrect.top, mSubrect.right, mSubrect.bottom };
  1287.             success = InternalBlt(pDest, &rDst, &rSrc, doNotWait, stillDrawing);
  1288.         } else
  1289.             success = InternalBlt(pDest, &rDst, NULL, doNotWait, stillDrawing);
  1290.     } else {
  1291.         const VDPixmap& source = mSource.pixmap;
  1292.         vdrect32 r;
  1293.         if (mbUseSubrect)
  1294.             r = mSubrect;
  1295.         else
  1296.             r.set(0, 0, source.w, source.h);
  1297.  
  1298.         const uint32 fieldmode = mode & kModeFieldMask;
  1299.  
  1300.         uint32 vinc        = (r.height() << 16) / rClient.bottom;
  1301.         uint32 vaccum    = (vinc >> 1) + (r.top << 16);
  1302.         uint32 vtlimit    = (((source.h + 1) >> 1) << 17) - 1;
  1303.         int fieldbase    = (fieldmode == kModeOddField ? 1 : 0);
  1304.         int ystep        = (fieldmode == kModeAllFields) ? 1 : 2;
  1305.  
  1306.         vaccum += vinc*fieldbase;
  1307.         vinc *= ystep;
  1308.  
  1309.         for(int y = fieldbase; y < rClient.bottom; y += ystep) {
  1310.             int v;
  1311.  
  1312.             if (y & 1) {
  1313.                 uint32 vt = vaccum < 0x8000 ? 0 : vaccum - 0x8000;
  1314.  
  1315.                 v = (y&1) + ((vt>>16) & ~1);
  1316.             } else {
  1317.                 uint32 vt = vaccum + 0x8000;
  1318.  
  1319.                 if (vt > vtlimit)
  1320.                     vt = vtlimit;
  1321.  
  1322.                 v = (vt>>16) & ~1;
  1323.             }
  1324.  
  1325.             RECT rDstTemp = { rDst.left, rDst.top+y, rDst.right, rDst.top+y+1 };
  1326.             RECT rSrcTemp = { r.left, v, r.width(), v+1 };
  1327.  
  1328.             if (!InternalBlt(pDest, &rDstTemp, &rSrcTemp, doNotWait || y > fieldbase, stillDrawing)) {
  1329.                 success = false;
  1330.                 break;
  1331.             }
  1332.  
  1333.             vaccum += vinc;
  1334.         }
  1335.     }
  1336.  
  1337.     if (doNotWait && stillDrawing)
  1338.         return false;
  1339.     
  1340.     if (pDest)
  1341.         pDest->SetClipper(NULL);
  1342.  
  1343.     mbPresentPending = false;
  1344.  
  1345.     if (mode & kModeVSync) {
  1346.         mPresentHistory.mbPresentPending = false;
  1347.  
  1348.         if (!success)
  1349.             return true;
  1350.  
  1351.         mPresentHistory.mAverageEndScanline += ((float)mPresentHistory.mLastScanline - mPresentHistory.mAverageEndScanline) * 0.01f;
  1352.         mPresentHistory.mAveragePresentTime += ((VDGetPreciseTick() - mPresentHistory.mPresentStartTime)*VDGetPreciseSecondsPerTick() - mPresentHistory.mAveragePresentTime) * 0.01f;
  1353.  
  1354.         IDirectDraw2 *pDD = mpddman->GetDDraw();
  1355.         DWORD scan2;
  1356.         bool inVBlank2 = false;
  1357.         HRESULT hr = pDD->GetScanLine(&scan2);
  1358.         if (hr == DDERR_VERTICALBLANKINPROGRESS) {
  1359.             scan2 = 0;
  1360.             inVBlank2 = true;
  1361.             hr = S_OK;
  1362.         }
  1363.  
  1364.         float syncDelta = 0.0f;
  1365.         if (SUCCEEDED(hr)) {
  1366.             float yf = ((float)scan2 - (float)mPresentHistory.mScanTop) / ((float)mPresentHistory.mScanBottom - (float)mPresentHistory.mScanTop);
  1367.  
  1368.             yf -= 0.2f;
  1369.  
  1370.             if (yf < 0.0f)
  1371.                 yf = 0.0f;
  1372.             if (yf > 1.0f)
  1373.                 yf = 1.0f;
  1374.  
  1375.             if (yf > 0.5f)
  1376.                 yf -= 1.0f;
  1377.  
  1378.             syncDelta = yf;
  1379.  
  1380.             int displayHeight = mpddman->GetPrimaryDesc().dwHeight;
  1381.  
  1382.             mPresentHistory.mScanlineTarget -= yf * 15.0f;
  1383.             if (mPresentHistory.mScanlineTarget < 0.0f)
  1384.                 mPresentHistory.mScanlineTarget += (float)displayHeight;
  1385.             else if (mPresentHistory.mScanlineTarget >= (float)displayHeight)
  1386.                 mPresentHistory.mScanlineTarget -= (float)displayHeight;
  1387.  
  1388.             float success = inVBlank2 || (int)scan2 <= mPresentHistory.mScanTop || (int)scan2 >= mPresentHistory.mScanBottom ? 1.0f : 0.0f;
  1389.  
  1390.             int zone = 0;
  1391.             if (!mPresentHistory.mbLastWasVBlank)
  1392.                 zone = ((int)mPresentHistory.mLastScanline * 16) / displayHeight;
  1393.  
  1394.             for(int i=0; i<17; ++i) {
  1395.                 if (i != zone)
  1396.                     mPresentHistory.mAttemptProb[i] *= 0.99f;
  1397.             }
  1398.  
  1399.             mPresentHistory.mAttemptProb[zone] += (1.0f - mPresentHistory.mAttemptProb[zone]) * 0.01f;
  1400.             mPresentHistory.mSuccessProb[zone] += (success - mPresentHistory.mSuccessProb[zone]) * 0.01f;
  1401.  
  1402.             if (mPresentHistory.mLastScanline < mPresentHistory.mScanTop) {
  1403.                 mPresentHistory.mVBlankSuccess += (success - mPresentHistory.mVBlankSuccess) * 0.01f;
  1404.             }
  1405.  
  1406.             if (!mPresentHistory.mbLastWasVBlank && !inVBlank2 && (int)scan2 > mPresentHistory.mLastScanline) {
  1407.                 float delta = (float)(int)(scan2 - mPresentHistory.mLastScanline);
  1408.  
  1409.                 mPresentHistory.mPresentDelay += (delta - mPresentHistory.mPresentDelay) * 0.01f;
  1410.             }
  1411.         }
  1412.     }
  1413.  
  1414.     // Workaround for Windows Vista DWM not adding window to composition tree immediately
  1415.     if (mbFirstFrame) {
  1416.         mbFirstFrame = false;
  1417.         SetWindowPos(mhwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_FRAMECHANGED);
  1418.     }
  1419.  
  1420.     mSource.mpCB->RequestNextFrame();
  1421.  
  1422.     return true;
  1423. }
  1424.  
  1425. bool VDVideoDisplayMinidriverDirectDraw::InternalBlt(IDirectDrawSurface2 *&pDest, RECT *prDst, RECT *prSrc, bool doNotWait, bool& stillDrawing) {
  1426.     HRESULT hr;
  1427.     DWORD flags = doNotWait ? DDBLT_ASYNC : DDBLT_ASYNC | DDBLT_WAIT;
  1428.     RECT rdstClip;
  1429.     RECT rsrcClip;
  1430.  
  1431.     if (prDst && !mbEnableSecondaryDraw) {
  1432.         // NVIDIA drivers have an annoying habit of glitching horribly when the blit rectangle extends outside of the
  1433.         // primary monitor onto a secondary, so we clip manually.
  1434.         const vdrect32& rclip = mpddman->GetMonitorRect();
  1435.  
  1436.         if (prDst->left >= prDst->right && prDst->bottom >= prDst->top) {
  1437.             stillDrawing = false;
  1438.             return true;
  1439.         }
  1440.  
  1441.         RECT rsrc0;
  1442.         if (prSrc)
  1443.             rsrc0 = *prSrc;
  1444.         else {
  1445.             rsrc0.left = 0;
  1446.             rsrc0.top = 0;
  1447.             rsrc0.right = mSource.pixmap.w;
  1448.             rsrc0.bottom = mSource.pixmap.h;
  1449.         }
  1450.  
  1451.         rsrcClip = rsrc0;
  1452.         int offsetL = prDst->left - rclip.left;
  1453.         int offsetT = prDst->top - rclip.top;
  1454.         int offsetR = rclip.right - prDst->right;
  1455.         int offsetB = rclip.bottom - prDst->bottom;
  1456.  
  1457.         if ((offsetL | offsetT | offsetR | offsetB) < 0) {
  1458.             rdstClip = *prDst;
  1459.  
  1460.             float xRatio = (float)(rsrc0.right - rsrc0.left) / (float)(rdstClip.right - rdstClip.left);
  1461.             float yRatio = (float)(rsrc0.bottom - rsrc0.top) / (float)(rdstClip.bottom - rdstClip.top);
  1462.  
  1463.             if (offsetL < 0) {
  1464.                 rdstClip.left = rclip.left;
  1465.                 rsrcClip.left -= VDRoundToInt(offsetL * xRatio);
  1466.             }
  1467.  
  1468.             if (offsetT < 0) {
  1469.                 rdstClip.top = rclip.top;
  1470.                 rsrcClip.top -= VDRoundToInt(offsetT * yRatio);
  1471.             }
  1472.  
  1473.             if (offsetR < 0) {
  1474.                 rdstClip.right = rclip.right;
  1475.                 rsrcClip.right += VDRoundToInt(offsetR * xRatio);
  1476.             }
  1477.  
  1478.             if (offsetB < 0) {
  1479.                 rdstClip.bottom = rclip.bottom;
  1480.                 rsrcClip.bottom += VDRoundToInt(offsetB * yRatio);
  1481.             }
  1482.  
  1483.             if (rdstClip.left >= rdstClip.right || rdstClip.top >= rdstClip.bottom) {
  1484.                 stillDrawing = false;
  1485.                 return true;
  1486.             }
  1487.  
  1488.             if (rsrcClip.right <= rsrcClip.left) {
  1489.                 rsrcClip.left = (rsrc0.left + rsrc0.right) >> 1;
  1490.                 rsrcClip.right = rsrcClip.left + 1;
  1491.             }
  1492.  
  1493.             if (rsrcClip.bottom <= rsrcClip.top) {
  1494.                 rsrcClip.top = (rsrc0.top + rsrc0.bottom) >> 1;
  1495.                 rsrcClip.bottom = rsrcClip.top + 1;
  1496.             }
  1497.  
  1498.             prDst = &rdstClip;
  1499.             prSrc = &rsrcClip;
  1500.         }
  1501.     }
  1502.  
  1503.     stillDrawing = false;
  1504.     for(;;) {
  1505.         hr = pDest->Blt(prDst, mpddsBitmap, prSrc, 0, NULL);
  1506.  
  1507.         if (hr == DDERR_WASSTILLDRAWING) {
  1508.             stillDrawing = true;
  1509.             return true;
  1510.         }
  1511.  
  1512.         if (SUCCEEDED(hr))
  1513.             break;
  1514.  
  1515.         if (hr != DDERR_SURFACELOST)
  1516.             break;
  1517.  
  1518.         if (FAILED(mpddsBitmap->IsLost())) {
  1519.             mpddsBitmap->Restore();
  1520.             mbValid = false;
  1521.             break;
  1522.         }
  1523.  
  1524.         if (FAILED(pDest->IsLost())) {
  1525.             pDest->SetClipper(NULL);
  1526.             pDest = NULL;
  1527.  
  1528.             if (!mpddman->Restore())
  1529.                 return false;
  1530.  
  1531.             if (mbReset)
  1532.                 return false;
  1533.  
  1534.             pDest = mpddman->GetPrimary();
  1535.             pDest->SetClipper(mpddc);
  1536.         }
  1537.     }
  1538.  
  1539.     return SUCCEEDED(hr);
  1540. }
  1541.