didevimg.cpp
上传用户:sycq158
上传日期:2008-10-22
资源大小:15361k
文件大小:95k
源码类别:

游戏

开发平台:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. // File: DIDevImg.cpp
  3. //
  4. // Desc: Implementation for CDIDevImage class, which encapsulates methods for 
  5. //       drawing device images, callout strings, and object highlights.
  6. //
  7. // Copyright( c ) Microsoft Corporation. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #include "DIDevImg.h"
  10. #include <d3dx9tex.h>
  11. //-----------------------------------------------------------------------------
  12. // Name: CDIDevImage
  13. // Desc: Constructor
  14. //-----------------------------------------------------------------------------
  15. CDIDevImage::CDIDevImage() 
  16. {
  17.     m_bInitialized  = FALSE;
  18.     m_bCustomUI     = FALSE;
  19.     m_bInvalidUI    = TRUE;
  20.     m_ahImages      = NULL;
  21.     m_dwNumViews    = 0;
  22.     m_dwNumObjects  = 0;
  23.     m_apObject      = NULL;
  24.     m_dwWidthPref   = 0;
  25.     m_dwHeightPref  = 0;
  26.     m_dwScaleMethod = 0;
  27.     m_BkColor       = D3DCOLOR_ARGB(255, 0, 0, 0);
  28.     m_hFont         = NULL;
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Name: ~CDIDevImage
  32. // Desc: Destructor
  33. //-----------------------------------------------------------------------------
  34. CDIDevImage::~CDIDevImage()
  35. {
  36.     CleanUp();
  37. }
  38. //-----------------------------------------------------------------------------
  39. // Name: Init
  40. // Desc: Responsible for initializing and preparing the CDIDevImage object for
  41. //       rendering the device image. Init must be called before the other
  42. //       public functions.
  43. // Args: pDIDevice - Pointer to a DirectInputDevice object for which the
  44. //         configuration UI should be created.
  45. //  Ret: DI_OK - Success; image found.
  46. //       DI_IMAGENOTFOUND - Success; no image. Default UI created instead.
  47. //       DIERR_INVALIDPARAM - Fail; invalid argument passed.
  48. //-----------------------------------------------------------------------------    
  49. HRESULT CDIDevImage::Init( LPDIRECTINPUTDEVICE8 pDIDevice )
  50. {
  51.     HRESULT hr = DI_OK;
  52.     
  53.     // Sanity check
  54.     if( NULL == pDIDevice )
  55.         return DIERR_INVALIDPARAM;
  56.     // Always start clean
  57.     CleanUp();
  58.     
  59.     // retrieve the image info from DirectInput
  60.     hr = LoadImageInfo( pDIDevice );
  61.     if( FAILED(hr) )
  62.     {
  63.         // Image information may have partially loaded. Clean out whatever
  64.         // is stored since we'll need to create a custom UI from scratch.
  65.         CleanUp();
  66.         // For one reason or another, the image info for this device
  67.         // could not be loaded. At this point, create a default UI.
  68.         m_bCustomUI = true;
  69.         hr = CreateCustomImageInfo( pDIDevice );
  70.         if( FAILED(hr) )
  71.         {
  72.             SAFE_RELEASE(pDIDevice);
  73.             CleanUp();
  74.             return hr;
  75.         }
  76.     }
  77.     
  78.     // Create the default callout font
  79.     CreateFont();
  80.     // Made it through initialization. Set the initialized flag to allow the
  81.     // other member functions to be called.
  82.     m_bInitialized = true;
  83.     // Both of these values indicate success, but the client may wish to know
  84.     // whether an image was actually found for this device, or if we are just
  85.     // producing a default UI.
  86.     return m_bCustomUI ? DI_IMAGENOTFOUND : DI_OK;
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Name: SetCalloutState
  90. // Desc: Sets the state for a specific callout
  91. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  92. //         dwType value returned by EnumDeviceObjects.
  93. //       dwCalloutState - New state of the callout, which may be zero or more
  94. //         combinations of:
  95. //         DIDICOS_HIGHLIGHTED - Overlay drawn. Callout drawn in high color.
  96. //         DIDICOS_INVISIBLE - Overlay and callout string not drawn.
  97. //         DIDICOS_TOOLTIP - Tooltip drawn if callout text is truncated.
  98. //  Ret: DI_OK - Success.
  99. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  100. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  101. //-----------------------------------------------------------------------------
  102. HRESULT CDIDevImage::SetCalloutState( DWORD dwObjId, DWORD dwCalloutState )
  103. {
  104.     // Verify initialization
  105.     if( false == m_bInitialized )
  106.         return DIERR_NOTINITIALIZED;
  107.     CDIDIObject* pObject = GetObject( dwObjId );
  108.     if( NULL == pObject )
  109.         return DIERR_OBJECTNOTFOUND;
  110.                  
  111.     DWORD dwOldState = pObject->GetCalloutState();
  112.     pObject->SetCalloutState( dwCalloutState );
  113.     // This action might change the layout for the custom UI
  114.     if( m_bCustomUI &&
  115.         ( DIDICOS_INVISIBLE & dwOldState ||
  116.           DIDICOS_INVISIBLE & dwCalloutState ) )
  117.           m_bInvalidUI = TRUE;
  118.     return DI_OK;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Name: GetCalloutState
  122. // Desc: Returns the state of a specific callout
  123. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  124. //         dwType value returned by EnumDeviceObjects.
  125. //       pdwCalloutState - Pointer to a variable which will contain the current
  126. //         callout value after a successful return.
  127. //  Ret: DI_OK - Success.
  128. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  129. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  130. //-----------------------------------------------------------------------------
  131. HRESULT CDIDevImage::GetCalloutState( DWORD dwObjId, LPDWORD pdwCalloutState )
  132. {
  133.     // Verify initialization
  134.     if( false == m_bInitialized )
  135.         return DIERR_NOTINITIALIZED;
  136.     CDIDIObject* pObject = GetObject( dwObjId );
  137.     if( NULL == pObject )
  138.         return DIERR_OBJECTNOTFOUND;
  139.     *pdwCalloutState = pObject->GetCalloutState();
  140.     return DI_OK;
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Name: SetCalloutColors
  144. // Desc: Sets the callout-unique colors to be used when painting a specific
  145. //       callout
  146. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  147. //         dwType value returned by EnumDeviceObjects.
  148. //       crColorNormal - Foreground text color for callout strings in a normal
  149. //         state.
  150. //       crColorHigh - Foreground text color for callout strings in a
  151. //         highlighted state.
  152. //  Ret: DI_OK - Success.
  153. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  154. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  155. //-----------------------------------------------------------------------------
  156. HRESULT CDIDevImage::SetCalloutColors( DWORD dwObjId, COLORREF crColorNormal, COLORREF crColorHigh )
  157. {
  158.     // Verify initialization
  159.     if( false == m_bInitialized )
  160.         return DIERR_NOTINITIALIZED;
  161.     CDIDIObject* pObject = GetObject( dwObjId );
  162.     if( NULL == pObject )
  163.         return DIERR_OBJECTNOTFOUND;
  164.     pObject->SetCalloutColors( crColorNormal, crColorHigh );
  165.     return DI_OK;
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Name: GetCalloutColors
  169. // Desc: Obtains the callout-unique colors used when painting a specific
  170. //       callout
  171. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  172. //         dwType value returned by EnumDeviceObjects.
  173. //       pcrColorNormal - Pointer to a COLORREF variable which will contain
  174. //         the normal callout color after a successful return. May be NULL.
  175. //       pcrColorHigh - Pointer to a COLORREF variable which will contain
  176. //         the highlight callout color after a successful return. May be NULL.
  177. //  Ret: DI_OK - Success.
  178. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  179. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  180. //-----------------------------------------------------------------------------
  181. HRESULT CDIDevImage::GetCalloutColors( DWORD dwObjId, LPCOLORREF pcrColorNormal, LPCOLORREF pcrColorHigh )
  182. {
  183.     // Verify initialization
  184.     if( false == m_bInitialized )
  185.         return DIERR_NOTINITIALIZED;
  186.     CDIDIObject* pObject = GetObject( dwObjId );
  187.     if( NULL == pObject )
  188.         return DIERR_OBJECTNOTFOUND;
  189.     pObject->GetCalloutColors( pcrColorNormal, pcrColorHigh );
  190.     return DI_OK;
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Name: SetCalloutText
  194. // Desc: Sets the text associated with the callout specified by an object ID
  195. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  196. //         dwType value returned by EnumDeviceObjects.
  197. //       strText - New callout text.
  198. //  Ret: DI_OK - Success.
  199. //       DIERR_INVALIDPARAM - Fail; Null pointer passed.
  200. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  201. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  202. //-----------------------------------------------------------------------------
  203. HRESULT CDIDevImage::SetCalloutText( DWORD dwObjId, LPCTSTR strText )
  204. {
  205.     // Verify initialization
  206.     if( false == m_bInitialized )
  207.         return DIERR_NOTINITIALIZED;
  208.     if( NULL == strText )
  209.         return DIERR_INVALIDPARAM;
  210.     CDIDIObject* pObject = GetObject( dwObjId );
  211.     if( NULL == pObject )
  212.         return DIERR_OBJECTNOTFOUND;
  213.     pObject->SetCalloutText( strText );
  214.     // This action might change the layout for the custom UI
  215.     if( m_bCustomUI )
  216.         m_bInvalidUI = TRUE;
  217.     return DI_OK;
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Name: GetCalloutText
  221. // Desc: Returns the text associated with a specific callout
  222. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  223. //         dwType value returned by EnumDeviceObjects.
  224. //       pstrText - Pointer to a string buffer which will collect the current
  225. //         callout string.
  226. //       dwSize - Maximum number of characters to copy into the buffer.
  227. //  Ret: DI_OK - Success.
  228. //       DIERR_INVALIDPARAM - Fail; Null pointer passed.
  229. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  230. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  231. //-----------------------------------------------------------------------------
  232. HRESULT CDIDevImage::GetCalloutText( DWORD dwObjId, LPTSTR pstrText, DWORD dwSize )
  233. {
  234.     // Verify initialization
  235.     if( false == m_bInitialized )
  236.         return DIERR_NOTINITIALIZED;
  237.     if( NULL == pstrText )
  238.         return DIERR_INVALIDPARAM;
  239.     CDIDIObject* pObject = GetObject( dwObjId );
  240.     if( NULL == pObject )
  241.         return DIERR_OBJECTNOTFOUND;
  242.     pObject->GetCalloutText( pstrText, dwSize );
  243.     return DI_OK;
  244. }
  245. //-----------------------------------------------------------------------------
  246. // Name: GetObjFromPoint
  247. // Desc: Returns the ID of the object on the device corresponding to the
  248. //       callout which contains the given point.
  249. // Args: Pt - Point to check against callout rect coordinates
  250. //       pdwObjId - Pointer to a variable which will contain the object ID upon
  251. //         successful return.
  252. //  Ret: DI_OK - Success.
  253. //       DIERR_INVALIDPARAM - Fail; Null pointer passed.
  254. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  255. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  256. //-----------------------------------------------------------------------------
  257. HRESULT CDIDevImage::GetObjFromPoint( POINT Pt, LPDWORD pdwObjId )
  258. {
  259.     // Verify initialization
  260.     if( false == m_bInitialized )
  261.         return DIERR_NOTINITIALIZED;
  262.     if( NULL == pdwObjId )
  263.         return DIERR_INVALIDPARAM;
  264.     if( m_dwActiveView > m_dwNumViews )
  265.         return E_FAIL;
  266.     // For a custom UI, this method depends on the UI being calculated
  267.     if( m_bCustomUI && m_bInvalidUI )
  268.         BuildCustomUI();
  269.     DIDICallout *pCallout = NULL;
  270.     // for each object
  271.     for( UINT i=0; i < m_dwNumObjects; i++ )
  272.     {
  273.         pCallout = m_apObject[i]->GetCallout( m_dwActiveView );
  274.         if( PtInRect( &(pCallout->rcScaled), Pt ) )
  275.         {
  276.             // if the point is inside the scaled bounding rect, the
  277.             // correct callout has been found.
  278.             *pdwObjId = m_apObject[i]->GetID();
  279.             return DI_OK;
  280.         }
  281.     }
  282.     return S_FALSE;
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Name: SetActiveView
  286. // Desc: Activates the provided view. This view will be painted when the 
  287. //       device image is rendered.
  288. // Args: dwView - The index of the new view.
  289. //  Ret: DI_OK - Success.
  290. //       DIERR_INVALIDPARAM - Fail; View is out of range.
  291. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  292. //-----------------------------------------------------------------------------
  293. HRESULT CDIDevImage::SetActiveView( DWORD dwView )
  294. {
  295.     // Verify initialization
  296.     if( false == m_bInitialized )
  297.         return DIERR_NOTINITIALIZED;
  298.     // For a custom UI, this method depends on the UI being calculated
  299.     if( m_bCustomUI && m_bInvalidUI )
  300.         BuildCustomUI();
  301.     // Make sure view exists
  302.     if( dwView >= m_dwNumViews )
  303.         return DIERR_INVALIDPARAM;
  304.     m_dwActiveView = dwView;
  305.     return DI_OK;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Name: GetActiveView
  309. // Desc: Returns the index of the currently active view. The active view is
  310. //       what CDIDevImage paints when the device image is rendered.
  311. // Args: pdwView - Pointer to a variable which will contain the current view
  312. //         index upon successful return. May be NULL.
  313. //       pdwNumViews - Pointer to a variable which will contain the total
  314. //         number of views upon successful return. May be NULL.
  315. //  Ret: DI_OK - Success.
  316. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  317. //-----------------------------------------------------------------------------
  318. HRESULT CDIDevImage::GetActiveView( LPDWORD pdwView, LPDWORD pdwNumViews )
  319. {
  320.     // Verify initialization
  321.     if( false == m_bInitialized )
  322.         return DIERR_NOTINITIALIZED;
  323.     // For a custom UI, this method depends on the UI being calculated
  324.     if( m_bCustomUI && m_bInvalidUI )
  325.         BuildCustomUI();
  326.     if( pdwView )
  327.         *pdwView = m_dwActiveView;
  328.     if( pdwNumViews )
  329.         *pdwNumViews = m_dwNumViews;
  330.     return DI_OK;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Name: GetViewForObj
  334. // Desc: Returns the index of the most appropriate view for a specific object
  335. // Args: dwObjId - Object ID of the requested callout. This corresponds to the
  336. //         dwType value returned by EnumDeviceObjects.
  337. //       pdwView - Pointer to a variable which will contain an appropriate
  338. //         view for the given object after return. May be NULL.
  339. //  Ret: DI_OK - Success.
  340. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  341. //       DIERR_INVALIDPARAM - Fail; Null pointer.
  342. //       DIERR_OBJECTNOTFOUND - Fail; Given ID not found in list of objects
  343. //-----------------------------------------------------------------------------
  344. HRESULT CDIDevImage::GetViewForObj( DWORD dwObjId, LPDWORD pdwView )
  345. {
  346.     UINT i=0; // Loop variable
  347.     // Verify initialization
  348.     if( false == m_bInitialized )
  349.         return DIERR_NOTINITIALIZED;
  350.     if( NULL == pdwView )
  351.         return DIERR_INVALIDPARAM;
  352.     CDIDIObject* pObject = GetObject( dwObjId );
  353.     if( NULL == pObject )
  354.         return DIERR_OBJECTNOTFOUND;
  355.     // For a custom UI, this method depends on the UI being calculated
  356.     if( m_bCustomUI && m_bInvalidUI )
  357.         BuildCustomUI();
  358.     // The method used to determine the best view for a particular object
  359.     // is simple: The view which has the largest overlay rectangle probably
  360.     // has the best view for an object. If there are no overlays for the
  361.     // given object, use the view with the largest callout rectangle, or
  362.     // the base view (0) if there are no callouts for the given object.
  363.     
  364.     DWORD dwCalloutMax = 0;
  365.     DWORD dwCalloutIndex = 0;
  366.     DWORD dwOverlayMax = 0;
  367.     DWORD dwOverlayIndex = 0;
  368.     for( i=0; i < m_dwNumViews; i++ )
  369.     {
  370.         DWORD dwArea = 0;
  371.         DIDIOverlay *pOverlay = pObject->GetOverlay( i );
  372.         DIDICallout *pCallout = pObject->GetCallout( i );
  373.         dwArea = ( pOverlay->rcScaled.right - pOverlay->rcScaled.left ) *
  374.                  ( pOverlay->rcScaled.bottom - pOverlay->rcScaled.top );
  375.         if( dwArea > dwOverlayMax )
  376.         {
  377.             dwOverlayMax = dwArea;
  378.             dwOverlayIndex = i;
  379.         }      
  380.         dwArea = ( pCallout->rcScaled.right - pCallout->rcScaled.left ) *
  381.                  ( pCallout->rcScaled.bottom - pCallout->rcScaled.top );
  382.         if( dwArea > dwCalloutMax )
  383.         {
  384.             dwCalloutMax = dwArea;
  385.             dwCalloutIndex = i;
  386.         }      
  387.     }
  388.     
  389.     // If an overlay rectangle was found, use the overlay index; otherwise,
  390.     // use the callout index (this will be 0 if no callouts were found ).
  391.     *pdwView = ( dwOverlayMax > 0 ) ? dwOverlayIndex : dwCalloutIndex;
  392.     
  393.     return DI_OK;
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Name: SetOutputImageSize
  397. // Desc: Sets the size of the image that CDIDevImage will paint and output to
  398. //       the application
  399. // Args: dwWidth - Preferred width.
  400. //       dwHeight - Preferred height.
  401. //       dwFlags - Scaling flag. Must be one of following:
  402. //         DIDISOIS_DEFAULT - dwWidth and dwHeight values are ignored. The image
  403. //           will not be scaled after loaded from disk; for created UIs, the
  404. //           size is determined by the global constants. This is the default
  405. //           value.                 
  406. //         DIDISOIS_RESIZE - Scale the image and callouts to the given width
  407. //           and height.
  408. //         DIDISOIS_MAINTAINASPECTUSINGWIDTH - Scale the image and callouts to
  409. //           the given width, but maintain the aspect ratio. The dwHeight 
  410. //           argument is ignored
  411. //         DIDISOIS_MAINTAINASPECTUSINGHEIGHT - Scale the image and callouts to
  412. //           the given height, but maintain the aspect ratio. The dwWidth
  413. //           argument is ignored.
  414. //  Ret: DI_OK - Success.
  415. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  416. //-----------------------------------------------------------------------------
  417. HRESULT CDIDevImage::SetOutputImageSize( DWORD dwWidth, DWORD dwHeight, DWORD dwFlags )
  418. {
  419.     // Verify initialization
  420.     if( false == m_bInitialized )
  421.         return DIERR_NOTINITIALIZED;
  422.     // Store arguments
  423.     m_dwWidthPref   = dwWidth;
  424.     m_dwHeightPref  = dwHeight;
  425.     m_dwScaleMethod = dwFlags;
  426.     // If the image size has changed, all the stored images are no longer valid
  427.     DestroyImages();
  428.     // This action might change the layout for the custom UI
  429.     if( m_bCustomUI )
  430.         m_bInvalidUI = TRUE;
  431.     return DI_OK;
  432. }
  433. //-----------------------------------------------------------------------------
  434. // Name: SetFont
  435. // Desc: Sets the font to be used for the callout text when rendering the image
  436. // Args: hFont - Handle to a GDI font object. The font's properties will be
  437. //         copied and used for drawn text.
  438. //  Ret: DI_OK - Success.
  439. //       DIERR_INVALIDPARAM - Fail; Invalid handle.
  440. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  441. //-----------------------------------------------------------------------------
  442. HRESULT CDIDevImage::SetFont( HFONT hFont )
  443. {
  444.     LOGFONT logfont = {0};
  445.     HFONT   hNewFont   = NULL;
  446.     // Verify initialization
  447.     if( false == m_bInitialized )
  448.         return DIERR_NOTINITIALIZED;
  449.  
  450.     // Retrieve the logfont and create a new member font
  451.     if( 0 == ::GetObject( hFont, sizeof(LOGFONT), &logfont ) )
  452.         return DIERR_INVALIDPARAM;
  453.     // Create a duplicate font
  454.     hNewFont = CreateFontIndirect( &logfont );
  455.     if( NULL == hNewFont )
  456.         return E_FAIL;
  457.     // Remove the current font
  458.     if( m_hFont )
  459.         DeleteObject( m_hFont );
  460.     // Copy the font handle
  461.     m_hFont = hNewFont;
  462.     // This action might change the layout for the custom UI
  463.     if( m_bCustomUI )
  464.         m_bInvalidUI = TRUE;
  465.     return DI_OK;
  466. }
  467. //-----------------------------------------------------------------------------
  468. // Name: SetColors
  469. // Desc: Assigns a set of colors to use when painting the various items on the
  470. //       image
  471. // Args: Background - Specifies the color and alpha component for the
  472. //         image background. Device images are stored in a format which allows
  473. //         the background to be replaced. D3D surfaces allow this background
  474. //         to contain transparency information. Alpha values of 0 thru 254 allow
  475. //         varying transparency effects on the output surfaces. A value
  476. //         of 255 is treated specially during alpha blending, such that the 
  477. //         final output image will fully opaque.
  478. //       crColorNormal - Foreground text color for callout strings in a normal
  479. //         state.
  480. //       crColorHigh - Foreground text color for callout strings in a
  481. //         highlighted state.
  482. //  Ret: DI_OK - Success.
  483. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  484. //-----------------------------------------------------------------------------
  485. HRESULT CDIDevImage::SetColors( D3DCOLOR Background, COLORREF crCalloutNormal, COLORREF crCalloutHigh )
  486. {
  487.     // Verify initialization
  488.     if( false == m_bInitialized )
  489.         return DIERR_NOTINITIALIZED;
  490.     // If the background is changing colors, the images will have to be 
  491.     // reloaded. As an optimization, the background color is only applied
  492.     // when the image is loaded since the background won't change as often as
  493.     // the image is rendered.
  494.     if( m_BkColor != Background )
  495.         DestroyImages();
  496.     
  497.     m_BkColor = Background;
  498.     for( UINT i=0; i < m_dwNumObjects; i++ )
  499.     {
  500.         m_apObject[i]->SetCalloutColors( crCalloutNormal, crCalloutHigh );
  501.     }
  502.     return DI_OK;
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Name: Render
  506. // Desc: Renders an image of the device and its callouts onto a Direct3DTexture
  507. // Args: pTexture - Pointer to a D3D Texture Object on which to paint the UI.
  508. //  Ret: DI_OK - Success.
  509. //       DIERR_INVALIDPARAM - Fail; Null pointer.
  510. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  511. //-----------------------------------------------------------------------------
  512. HRESULT CDIDevImage::Render( LPDIRECT3DTEXTURE9 pTexture )
  513. {
  514.     HRESULT hr = DI_OK;
  515.     LPDIRECT3DSURFACE9 pSurface = NULL;
  516.     // Verify initialization
  517.     if( false == m_bInitialized )
  518.         return DIERR_NOTINITIALIZED;
  519.     // Check parameters
  520.     if( NULL == pTexture )
  521.         return DIERR_INVALIDPARAM;
  522.     // Add a reference to the passed texture
  523.     pTexture->AddRef();
  524.     // Extract the surface
  525.     hr = pTexture->GetSurfaceLevel( 0, &pSurface );
  526.     if( FAILED(hr) )
  527.         goto LCleanReturn;
  528.     // Perform the render
  529.     if( m_bCustomUI )
  530.         hr = RenderCustomToTarget( (LPVOID) pSurface, DIDIRT_SURFACE );
  531.     else
  532.         hr = RenderToTarget( (LPVOID) pSurface, DIDIRT_SURFACE );
  533. LCleanReturn:
  534.     SAFE_RELEASE( pSurface );
  535.     SAFE_RELEASE( pTexture );
  536.     return hr;
  537. }
  538. //-----------------------------------------------------------------------------
  539. // Name: RenderToDC
  540. // Desc: Renders an image of the device and its callouts onto a GDI device
  541. //       object
  542. // Args: hDC - Handle to a device context on which to paint the UI.
  543. //  Ret: DI_OK - Success.
  544. //       DIERR_NOTINITIALIZED - Fail; Init() must be called first.
  545. //-----------------------------------------------------------------------------
  546. HRESULT CDIDevImage::RenderToDC( HDC hDC )
  547. {
  548.     HRESULT hr = S_OK;
  549.     // Verify initialization
  550.     if( false == m_bInitialized )
  551.         return DIERR_NOTINITIALIZED;
  552.     if( m_bCustomUI )
  553.         hr = RenderCustomToTarget( (LPVOID) hDC, DIDIRT_DC );
  554.     else
  555.         hr = RenderToTarget( (LPVOID) hDC, DIDIRT_DC );
  556.     return hr;
  557. }
  558. //-----------------------------------------------------------------------------
  559. // Name: RenderCustomToTarget
  560. // Desc: Renders a custom UI
  561. //-----------------------------------------------------------------------------
  562. HRESULT CDIDevImage::RenderCustomToTarget( LPVOID pvTarget, DIDIRENDERTARGET eTarget )
  563. {
  564.     HRESULT    hr         = DI_OK;
  565.     UINT       i          = 0;
  566.     HDC        hdcRender  = NULL;
  567.     HDC        hdcAlpha   = NULL;
  568.     RECT       rcBitmap   = {0};
  569.     DIBSECTION info       = {0};
  570.     HBITMAP    hbmpRender = NULL;
  571.     HBITMAP    hbmpAlpha  = NULL;
  572.     SIZE       size       = {0};
  573.     // Get the UI dimensions
  574.     GetCustomUISize( &size );
  575.     if( m_bInvalidUI )
  576.         BuildCustomUI();
  577.     // Create a background image
  578.     BITMAPINFO bmi = {0};
  579.     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  580.     bmi.bmiHeader.biWidth = size.cx;
  581.     bmi.bmiHeader.biHeight = - (int) size.cy; // top-down
  582.     bmi.bmiHeader.biPlanes = 1;
  583.     bmi.bmiHeader.biBitCount = 32;
  584.     bmi.bmiHeader.biCompression = BI_RGB;
  585.     hdcRender = CreateCompatibleDC( NULL );
  586.     if( NULL == hdcRender )
  587.     {
  588.         hr = DIERR_OUTOFMEMORY;
  589.         goto LCleanReturn;
  590.     }
  591.     hdcAlpha = CreateCompatibleDC( NULL );
  592.     if( NULL == hdcAlpha )
  593.     {
  594.         hr = DIERR_OUTOFMEMORY;
  595.         goto LCleanReturn;
  596.     }
  597.     
  598.     hbmpRender = CreateDIBSection( hdcRender, &bmi, DIB_RGB_COLORS, NULL, NULL, NULL );
  599.     if( hbmpRender == NULL )
  600.     {
  601.         hr = DIERR_OUTOFMEMORY;
  602.         goto LCleanReturn;
  603.     }
  604.     if( 0 == ::GetObject( hbmpRender, sizeof( DIBSECTION ), &info ) )
  605.     {
  606.         hr = E_FAIL;
  607.         goto LCleanReturn;
  608.     }
  609.     
  610.     SelectObject( hdcRender, hbmpRender );
  611.     SelectObject( hdcRender, m_hFont );
  612.     SetBkMode( hdcRender, TRANSPARENT );
  613.     FillBackground( hbmpRender, m_BkColor );
  614.     // Create a bitmap to store the alpha channel for the bitmap. Since GDI
  615.     // doesn't support alpha information, everything is drawn fully transparent.
  616.     // As a workaround, whenever a 2D method is called on the render dc,
  617.     // the same method will be called on the alpha dc. Before transfering the
  618.     // image to the provided surface, the transparency information will be 
  619.     // restored from the alpha bitmap. 
  620.     hbmpAlpha = CreateDIBSection( hdcAlpha, &bmi, DIB_RGB_COLORS, NULL, NULL, NULL );
  621.     if( NULL == hbmpAlpha )
  622.     {
  623.         hr = DIERR_OUTOFMEMORY;
  624.         goto LCleanReturn;
  625.     }
  626.     // Clear the alpha channel
  627.     DIBSECTION infoAlpha;
  628.     if( 0 == ::GetObject( hbmpAlpha, sizeof(DIBSECTION), &infoAlpha ) )
  629.     {
  630.         hr = E_FAIL;
  631.         goto LCleanReturn;
  632.     }
  633.     ZeroMemory( infoAlpha.dsBm.bmBits, infoAlpha.dsBm.bmWidthBytes * infoAlpha.dsBm.bmHeight );
  634.     SelectObject( hdcAlpha, hbmpAlpha );
  635.     SetBkMode( hdcAlpha, TRANSPARENT );
  636.     SetTextColor( hdcAlpha, RGB(255, 255, 255) );
  637.     SelectObject( hdcAlpha, GetStockObject( WHITE_PEN ) );
  638.     SelectObject( hdcAlpha, m_hFont );
  639.         
  640.     // Draw callout and object text
  641.     for( i=0; i < m_dwNumObjects; i++ )
  642.     {
  643.         COLORREF crNorm, crHigh, crCur;
  644.         
  645.         DIDICallout *pCallout = m_apObject[i]->GetCallout( m_dwActiveView );
  646.         DIDIOverlay *pOverlay = m_apObject[i]->GetOverlay( m_dwActiveView );
  647.         MAXSTRING    strCallout = {0};
  648.         MAXSTRING    strObject  = {0};
  649.         RECT rcFill = {0};
  650.         
  651.         // Callout may be invisible
  652.         if( DIDICOS_INVISIBLE & m_apObject[i]->GetCalloutState() )
  653.             continue;
  654.         if( IsRectEmpty( &pOverlay->rcScaled ) ||
  655.             IsRectEmpty( &pCallout->rcScaled ) )
  656.             continue;
  657.         m_apObject[i]->GetCalloutText( strCallout, MAX_PATH );
  658.         m_apObject[i]->GetName( strObject, MAX_PATH );
  659.         m_apObject[i]->GetCalloutColors( &crNorm, &crHigh );
  660.         crCur = ( DIDICOS_HIGHLIGHTED & m_apObject[i]->GetCalloutState() ) ? crHigh : crNorm;
  661.         
  662.         SetTextColor( hdcRender, crNorm );
  663.         
  664.         DWORD dwFlags = DT_TOP | DT_END_ELLIPSIS | DT_NOCLIP;
  665.         // Get the fill rect
  666.         rcFill = pOverlay->rcScaled;
  667.         DrawText( hdcRender, strObject, lstrlen( strObject ),
  668.                   &rcFill, dwFlags | DT_CALCRECT );
  669.         // Position the fill rect
  670.         rcFill.left += pOverlay->rcScaled.right - rcFill.right;
  671.         rcFill.right += pOverlay->rcScaled.right - rcFill.right;
  672.         // Inflate the fill rect
  673.         InflateRect( &rcFill, 5, 5 );
  674.         // But make sure the rect still fits on the screen
  675.         rcFill.left   = max( rcFill.left,   0 );
  676.         rcFill.top    = max( rcFill.top,    0 );
  677.         rcFill.right  = min( rcFill.right,  info.dsBm.bmWidth );
  678.         rcFill.bottom = min( rcFill.bottom, info.dsBm.bmHeight );
  679.         // Draw the object text
  680.         DrawText( hdcRender, strObject, lstrlen( strObject ),
  681.                   &(pOverlay->rcScaled), dwFlags | DT_RIGHT );
  682.         if( hdcAlpha )
  683.             DrawText( hdcAlpha, strObject, lstrlen( strObject ),
  684.                       &(pOverlay->rcScaled), dwFlags | DT_RIGHT );
  685.         
  686.         SetTextColor( hdcRender, crCur );
  687.         
  688.         // Get the fill rect
  689.         rcFill = pCallout->rcScaled;
  690.         DrawText( hdcRender, strCallout, lstrlen( strCallout ),
  691.                   &rcFill, dwFlags | DT_CALCRECT );
  692.         // Inflate the fill rect
  693.         InflateRect( &rcFill, 5, 5 );
  694.         // But make sure the rect still fits on the screen
  695.         rcFill.left   = max( rcFill.left,   0 );
  696.         rcFill.top    = max( rcFill.top,    0 );
  697.         rcFill.right  = min( rcFill.right,  info.dsBm.bmWidth );
  698.         rcFill.bottom = min( rcFill.bottom, info.dsBm.bmHeight );
  699.         // Draw the callout text
  700.         DrawText( hdcRender, strCallout, lstrlen( strCallout ),
  701.                   &(pCallout->rcScaled), dwFlags | DT_LEFT );
  702.         if( hdcAlpha )
  703.             DrawText( hdcAlpha, strCallout, lstrlen( strCallout ),
  704.                       &(pCallout->rcScaled), dwFlags | DT_LEFT );
  705.         // If the TOOLTIP flag is set and the callout text doesn't fit within
  706.         // the scaled rect, we need to draw the full text
  707.         if( DIDICOS_TOOLTIP & m_apObject[i]->GetCalloutState() )
  708.         {
  709.             SIZE TextSize = {0};
  710.             
  711.             // This string was modified by the first call to draw text, so we
  712.             // need to get a fresh copy
  713.             m_apObject[i]->GetCalloutText( strCallout, MAX_PATH-4 );
  714.             GetTextExtentPoint32( hdcRender, strCallout, lstrlen( strCallout ), &TextSize );
  715.             if( TextSize.cx > ( pCallout->rcScaled.right - pCallout->rcScaled.left ) )
  716.             {
  717.                 // Yep, the text is too big and is marked as a tooltip candidate.
  718.                 RECT rcBitmap = { 0, 0, info.dsBm.bmWidth, info.dsBm.bmHeight };
  719.                 DrawTooltip( hdcRender, hdcAlpha, strCallout, &rcBitmap, &(pCallout->rcScaled),
  720.                              CRFromColor( m_BkColor ), crNorm, crHigh );
  721.                 
  722.             }
  723.         }
  724.     }
  725.     // Finalize rendering
  726.     GdiFlush();
  727.     // Copy the freshly rendered image to the render target
  728.     switch( eTarget )
  729.     {
  730.         case DIDIRT_SURFACE:
  731.             // Since the image is being transfered to a Direct3D surface, the stored
  732.             // alpha information could be used.
  733.             ApplyAlphaChannel( hbmpRender, hbmpAlpha, ( (m_BkColor & ALPHA_MASK) == ALPHA_MASK ) );
  734.             rcBitmap.right  = info.dsBm.bmWidth;
  735.             rcBitmap.bottom = info.dsBm.bmHeight;
  736.             hr = D3DXLoadSurfaceFromMemory( (LPDIRECT3DSURFACE9) pvTarget,
  737.                                              NULL, NULL, info.dsBm.bmBits,
  738.                                              D3DFMT_A8R8G8B8, 
  739.                                              info.dsBm.bmWidthBytes,
  740.                                              NULL, &rcBitmap, 
  741.                                              D3DX_FILTER_NONE, 0 );
  742.             break;
  743.         case DIDIRT_DC:
  744.             BitBlt( (HDC) pvTarget, 0, 0, info.dsBm.bmWidth, info.dsBm.bmHeight,
  745.                           hdcRender, 0, 0, SRCCOPY );
  746.             break;
  747.     
  748.         default:
  749.             // Invalid render target
  750.             hr = DIERR_INVALIDPARAM;
  751.             goto LCleanReturn;
  752.     }   
  753. LCleanReturn:
  754.     
  755.     DeleteDC( hdcRender );
  756.     DeleteDC( hdcAlpha );
  757.     
  758.     if( hbmpAlpha )
  759.         DeleteObject( hbmpAlpha );
  760.     if( hbmpRender )
  761.         DeleteObject( hbmpRender );
  762.     return hr;
  763. }
  764. //-----------------------------------------------------------------------------
  765. // Name: RenderToTarget
  766. // Desc: Renders an image of the device and its callouts 
  767. //-----------------------------------------------------------------------------
  768. HRESULT CDIDevImage::RenderToTarget( LPVOID pvTarget, DIDIRENDERTARGET eTarget )
  769. {
  770.     HRESULT    hr           = DI_OK;
  771.     UINT       i            = 0; // Loop variable
  772.     RECT       rcBitmap     = {0};
  773.     HDC        hdcRender    = NULL;
  774.     HDC        hdcAlpha     = NULL;
  775.     DIBSECTION info         = {0};
  776.     
  777.     LPBYTE     pSavedPixels = NULL;
  778.     LPBYTE     pCleanPixels = NULL;
  779.     
  780.     HBITMAP    hbmpAlpha    = NULL;
  781.     BITMAPINFO bmi = {0};
  782.     
  783.     
  784.     // Verify initialization
  785.     if( false == m_bInitialized )
  786.         return DIERR_NOTINITIALIZED;
  787.     // Verify parameters
  788.     if( NULL == pvTarget )
  789.         return DIERR_INVALIDPARAM;
  790.     // Sanity check
  791.     if( m_dwActiveView >= m_dwNumViews )
  792.         return E_FAIL;
  793.     // Load images if not loaded already
  794.     if( NULL == m_ahImages[ m_dwActiveView ] )
  795.     {  
  796.         // File UI not yet loaded
  797.         if( FAILED( hr = LoadImages() ) )
  798.             return hr;
  799.     } 
  800.     
  801.     // Get information about the background image.
  802.     if( 0 == ::GetObject( m_ahImages[ m_dwActiveView ], sizeof(DIBSECTION), &info ) )
  803.         return E_FAIL;
  804.     // Allocate space for the saved background images
  805.     pSavedPixels = new BYTE[ info.dsBm.bmWidthBytes * info.dsBm.bmHeight ];
  806.     if( NULL == pSavedPixels )
  807.     {
  808.         hr = DIERR_OUTOFMEMORY;
  809.         goto LCleanReturn;
  810.     }
  811.     pCleanPixels = new BYTE[ info.dsBm.bmWidthBytes * info.dsBm.bmHeight ];
  812.     if( NULL == pCleanPixels )
  813.     {
  814.         // Could not create a copy of the background image; release memory
  815.         // here to avoid using unitialized pixels during cleanup.
  816.         SAFE_DELETE_ARRAY( pSavedPixels );
  817.         hr = DIERR_OUTOFMEMORY; 
  818.         goto LCleanReturn;
  819.     }
  820.     // Save the background
  821.     CopyMemory( pSavedPixels, info.dsBm.bmBits, 
  822.                 info.dsBm.bmWidthBytes * info.dsBm.bmHeight );
  823.     
  824.     // Draw overlays
  825.     for( i=0; i < m_dwNumObjects; i++ )
  826.     {  
  827.         DIDIOverlay *pOverlay = m_apObject[i]->GetOverlay( m_dwActiveView );
  828.         
  829.         if( DIDICOS_HIGHLIGHTED & m_apObject[i]->GetCalloutState() )
  830.         {     
  831.             // Draw overlay
  832.             if( pOverlay->hImage )    
  833.                 ApplyOverlay( m_ahImages[ m_dwActiveView ], &pOverlay->rcScaled, pOverlay->hImage );      
  834.         }
  835.     }
  836.     // Before drawing callouts and lines on top of the composed image, save
  837.     // a copy of the image bits. This will allow us to erase portions of lines
  838.     // which intersect with the callout text.
  839.     CopyMemory( pCleanPixels, info.dsBm.bmBits, 
  840.                 info.dsBm.bmWidthBytes * info.dsBm.bmHeight );
  841.   
  842.     // Load the background image into a device context for rendering
  843.     hdcRender = CreateCompatibleDC( NULL );
  844.     SelectObject( hdcRender, m_ahImages[ m_dwActiveView ] );
  845.     
  846.     SelectObject( hdcRender, m_hFont );
  847.     SetBkMode( hdcRender, TRANSPARENT );
  848.     SetBkColor( hdcRender, CRFromColor(m_BkColor) );
  849.   
  850.     hdcAlpha = CreateCompatibleDC( NULL );
  851.     // Create a bitmap to store the alpha channel for the bitmap. Since GDI
  852.     // doesn't support alpha information, everything is drawn fully transparent.
  853.     // As a workaround, whenever a 2D method is called on the render dc,
  854.     // the same method will be called on the alpha dc. Before transfering the
  855.     // image to the provided surface, the transparency information will be 
  856.     // restored from the alpha bitmap. 
  857.     
  858.     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  859.     bmi.bmiHeader.biWidth = info.dsBm.bmWidth;
  860.     bmi.bmiHeader.biHeight = - (int) info.dsBm.bmHeight; // top-down
  861.     bmi.bmiHeader.biPlanes = 1;
  862.     bmi.bmiHeader.biBitCount = 32;
  863.     bmi.bmiHeader.biCompression = BI_RGB;
  864.     hbmpAlpha = CreateDIBSection( hdcAlpha, &bmi, DIB_RGB_COLORS, NULL, NULL, NULL );
  865.     if( NULL == hbmpAlpha )
  866.     {
  867.         hr = DIERR_OUTOFMEMORY;
  868.         goto LCleanReturn;
  869.     }
  870.     // Clear the alpha channel
  871.     DIBSECTION infoAlpha;
  872.     if( 0 == ::GetObject( hbmpAlpha, sizeof(DIBSECTION), &infoAlpha ) )
  873.     {
  874.         hr = E_FAIL;
  875.         goto LCleanReturn;
  876.     }
  877.     ZeroMemory( infoAlpha.dsBm.bmBits, infoAlpha.dsBm.bmWidthBytes * infoAlpha.dsBm.bmHeight );
  878.     SelectObject( hdcAlpha, hbmpAlpha );
  879.     SetBkMode( hdcAlpha, TRANSPARENT );
  880.     SetTextColor( hdcAlpha, RGB(255, 255, 255) );
  881.     SelectObject( hdcAlpha, GetStockObject( WHITE_PEN ) );
  882.     SelectObject( hdcAlpha, m_hFont );
  883.      
  884.     // Draw callout lines
  885.     for( i=0; i < m_dwNumObjects; i++ )
  886.     {
  887.         COLORREF crNorm, crHigh, crCur;
  888.         POINT    aptArrow[2] = {0}; 
  889.         BOOL     bDrawArrow = FALSE;
  890.         
  891.         // Get the current callout
  892.         DIDICallout *pCallout = m_apObject[i]->GetCallout( m_dwActiveView );
  893.         // Callout may be invisible
  894.         if( DIDICOS_INVISIBLE & m_apObject[i]->GetCalloutState() )
  895.             continue;
  896.         
  897.         // Retrieve normal/highlighted colors
  898.         m_apObject[i]->GetCalloutColors( &crNorm, &crHigh );
  899.         // Set the current color based on callout state
  900.         crCur = ( DIDICOS_HIGHLIGHTED & m_apObject[i]->GetCalloutState() ) ? crHigh : crNorm;
  901.        
  902.         DeleteObject( SelectObject( hdcRender, CreatePen( PS_SOLID, 3, CRFromColor(m_BkColor) ) ) ); 
  903.         DeleteObject( SelectObject( hdcAlpha, CreatePen( PS_SOLID, 3, RGB(255, 255, 255) ) ) ); 
  904.         // Draw callout lines
  905.         MoveToEx( hdcRender, pCallout->aptLineScaled[0].x, pCallout->aptLineScaled[0].y, NULL );
  906.         MoveToEx( hdcAlpha, pCallout->aptLineScaled[0].x, pCallout->aptLineScaled[0].y, NULL );
  907.         
  908.         for( UINT j=1; j < pCallout->dwNumPoints; j++ )
  909.         {
  910.             LineTo( hdcRender, pCallout->aptLineScaled[j].x, pCallout->aptLineScaled[j].y );
  911.             LineTo( hdcAlpha, pCallout->aptLineScaled[j].x, pCallout->aptLineScaled[j].y );
  912.         }
  913.         // Draw arrow ends
  914.         if( pCallout->dwNumPoints >= 2 )
  915.         {
  916.             DWORD dwEnd = pCallout->dwNumPoints-1;
  917.             POINT p1 = pCallout->aptLineScaled[ dwEnd ];
  918.             POINT p2 = pCallout->aptLineScaled[ dwEnd-1 ];
  919.             aptArrow[0] = aptArrow[1] = p1;
  920.             bDrawArrow = TRUE;
  921.             aptArrow[0].x -= 1;
  922.             aptArrow[0].y -= 1;
  923.             aptArrow[1].x += 1;
  924.             aptArrow[1].y += 1;
  925.             
  926.             // Adjust arrow points based on line orientation
  927.             if( p1.y == p2.y )
  928.             {
  929.                 if( p2.x < p1.x )
  930.                     aptArrow[1].x -= 2;
  931.                 else
  932.                     aptArrow[0].x += 2;
  933.             }
  934.             else if( p1.x == p2.x )
  935.             {
  936.                 if( p2.y < p1.y )
  937.                     aptArrow[1].y -= 2;
  938.                 else
  939.                     aptArrow[0].y += 2;
  940.             }
  941.             else
  942.             {
  943.                 // This is a diagonal line. Skip the arrow endpoint.
  944.                 bDrawArrow = FALSE;
  945.             }
  946.             if( bDrawArrow )
  947.             {
  948.                 MoveToEx( hdcRender, aptArrow[0].x, aptArrow[0].y, NULL );
  949.                 LineTo(   hdcRender, aptArrow[1].x, aptArrow[1].y );
  950.                 MoveToEx( hdcAlpha, aptArrow[0].x, aptArrow[0].y, NULL );
  951.                 LineTo(   hdcAlpha, aptArrow[1].x, aptArrow[1].y );
  952.             }
  953.         }
  954.         // Select a new pen into the DC based on current color
  955.         DeleteObject( SelectObject( hdcRender, CreatePen( PS_SOLID, 1, crCur ) ) ); 
  956.         DeleteObject( SelectObject( hdcAlpha, GetStockObject( WHITE_PEN ) ) ); 
  957.         
  958.         // Draw callout lines
  959.         MoveToEx( hdcRender, pCallout->aptLineScaled[0].x, pCallout->aptLineScaled[0].y, NULL );
  960.         MoveToEx( hdcAlpha, pCallout->aptLineScaled[0].x, pCallout->aptLineScaled[0].y, NULL );
  961.         for( j=1; j < pCallout->dwNumPoints; j++ )
  962.         {
  963.             LineTo( hdcRender, pCallout->aptLineScaled[j].x, pCallout->aptLineScaled[j].y );
  964.             LineTo( hdcAlpha, pCallout->aptLineScaled[j].x, pCallout->aptLineScaled[j].y );
  965.         }
  966.         // Draw arrow ends
  967.         if( bDrawArrow )
  968.         {
  969.             DWORD dwEnd = pCallout->dwNumPoints-1;
  970.             SetPixel( hdcRender, aptArrow[0].x, aptArrow[0].y, crCur );
  971.             SetPixel( hdcRender, aptArrow[1].x, aptArrow[1].y, crCur );
  972.             SetPixel( hdcRender, pCallout->aptLineScaled[ dwEnd ].x, 
  973.                                  pCallout->aptLineScaled[ dwEnd ].y, crCur );
  974.             SetPixel( hdcAlpha, aptArrow[0].x, aptArrow[0].y, RGB(255, 255, 255) );
  975.             SetPixel( hdcAlpha, aptArrow[1].x, aptArrow[1].y, RGB(255, 255, 255) );
  976.             SetPixel( hdcAlpha, pCallout->aptLineScaled[ dwEnd ].x, 
  977.                                  pCallout->aptLineScaled[ dwEnd ].y, RGB(255, 255, 255) );
  978.         }
  979.         
  980.     }
  981.     
  982.     // Free the pen resource
  983.     DeleteObject( SelectObject( hdcRender, GetStockObject( WHITE_PEN ) ) );
  984.     DeleteObject( SelectObject( hdcAlpha, GetStockObject( WHITE_PEN ) ) );
  985.     // Draw callout text
  986.     for( i=0; i < m_dwNumObjects; i++ )
  987.     {
  988.         COLORREF crNorm, crHigh, crCur;
  989.         RECT     rcFill;
  990.         
  991.         DIDICallout *pCallout = m_apObject[i]->GetCallout( m_dwActiveView );
  992.         MAXSTRING    strCallout = {0};
  993.         
  994.         // Callout may be invisible
  995.         if( DIDICOS_INVISIBLE & m_apObject[i]->GetCalloutState() )
  996.             continue;
  997.         m_apObject[i]->GetCalloutText( strCallout, MAX_PATH-4 );
  998.         m_apObject[i]->GetCalloutColors( &crNorm, &crHigh );
  999.         
  1000.         
  1001.         
  1002.         if( IsRectEmpty( &pCallout->rcScaled ) )
  1003.             continue;
  1004.         // Draw callouts
  1005.         DWORD dwFormat = DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP;
  1006.         // Get text dimensions
  1007.         rcFill = pCallout->rcScaled;
  1008.         DrawText( hdcRender, strCallout, lstrlen( strCallout ),
  1009.                   &rcFill, dwFormat | DT_CALCRECT | DT_MODIFYSTRING );
  1010.       
  1011.         // Horizontal alignment
  1012.         if( pCallout->dwTextAlign & DIDAL_CENTERED      ) 
  1013.         {
  1014.             dwFormat |= DT_CENTER;
  1015.             OffsetRect( &rcFill, (pCallout->rcScaled.right - rcFill.right) / 2, 0 );
  1016.         }
  1017.         else if( pCallout->dwTextAlign & DIDAL_RIGHTALIGNED  ) 
  1018.         {
  1019.             dwFormat |= DT_RIGHT;
  1020.             OffsetRect( &rcFill, (pCallout->rcScaled.right - rcFill.right ), 0 );
  1021.         }
  1022.         else
  1023.         {
  1024.             dwFormat |= DT_LEFT;
  1025.         }
  1026.         // Vertical alignment
  1027.         if( pCallout->dwTextAlign & DIDAL_MIDDLE        ) 
  1028.         {
  1029.             dwFormat |= DT_VCENTER;
  1030.             OffsetRect( &rcFill, 0, (pCallout->rcScaled.bottom - rcFill.bottom) / 2 );
  1031.         }
  1032.         else if( pCallout->dwTextAlign & DIDAL_BOTTOMALIGNED ) 
  1033.         {
  1034.             dwFormat |= DT_BOTTOM;
  1035.             OffsetRect( &rcFill, 0, (pCallout->rcScaled.bottom - rcFill.bottom) );
  1036.         }
  1037.         else
  1038.         {
  1039.             dwFormat |= DT_TOP;
  1040.         }
  1041.         // First replace the background area behind the text to cover up
  1042.         // intersecting lines
  1043.         
  1044.         // Pad the returned rect
  1045.         InflateRect( &rcFill, 5, 5 );
  1046.         // But make sure the rect still fits on the screen
  1047.         rcFill.left   = max( rcFill.left,   0 );
  1048.         rcFill.top    = max( rcFill.top,    0 );
  1049.         rcFill.right  = min( rcFill.right,  info.dsBm.bmWidth );
  1050.         rcFill.bottom = min( rcFill.bottom, info.dsBm.bmHeight );
  1051.         RestoreRect( m_ahImages[ m_dwActiveView ], &rcFill, pCleanPixels );
  1052.         if( hdcAlpha )
  1053.             FillRect( hdcAlpha, &rcFill, (HBRUSH) GetStockObject( BLACK_BRUSH ) );
  1054.         // Fill behind the text
  1055.         SetTextColor( hdcRender, CRFromColor(m_BkColor) );
  1056.         for( int x = -1; x <= 1; x++ )
  1057.         {
  1058.             for( int y = -1; y <= 1; y++ )
  1059.             {
  1060.                 RECT rcText = pCallout->rcScaled;
  1061.                 OffsetRect( &rcText, x, y );
  1062.                 DrawText( hdcRender, strCallout, lstrlen( strCallout ),
  1063.                   &rcText, dwFormat );
  1064.                 DrawText( hdcAlpha, strCallout, lstrlen( strCallout ),
  1065.                   &rcText, dwFormat );
  1066.             }
  1067.         }
  1068.         // Now draw the actual text
  1069.         crCur = ( DIDICOS_HIGHLIGHTED & m_apObject[i]->GetCalloutState() ) ? crHigh : crNorm;     
  1070.         SetTextColor( hdcRender, crCur );
  1071.         DrawText( hdcRender, strCallout, lstrlen( strCallout ),
  1072.                   &(pCallout->rcScaled), dwFormat );
  1073.         
  1074.         DrawText( hdcAlpha, strCallout, lstrlen( strCallout ),
  1075.                   &(pCallout->rcScaled), dwFormat );
  1076.         
  1077.         // If the TOOLTIP flag is set and the callout text doesn't fit within
  1078.         // the scaled rect, we need to draw the full text
  1079.         if( DIDICOS_TOOLTIP & m_apObject[i]->GetCalloutState() )
  1080.         {
  1081.             SIZE TextSize = {0};
  1082.             
  1083.             // This string was modified by the first call to draw text, so we
  1084.             // need to get a fresh copy
  1085.             m_apObject[i]->GetCalloutText( strCallout, MAX_PATH-4 );
  1086.             GetTextExtentPoint32( hdcRender, strCallout, lstrlen( strCallout ), &TextSize );
  1087.             if( TextSize.cx > ( pCallout->rcScaled.right - pCallout->rcScaled.left ) )
  1088.             {
  1089.                 // Yep, the text is too big and is marked as a tooltip candidate.
  1090.                 RECT rcBitmap = { 0, 0, info.dsBm.bmWidth, info.dsBm.bmHeight };
  1091.                 DrawTooltip( hdcRender, hdcAlpha, strCallout, &rcBitmap, &(pCallout->rcScaled),
  1092.                              CRFromColor( m_BkColor ), crNorm, crHigh );
  1093.                 
  1094.             }
  1095.         }
  1096.     }
  1097.     
  1098.     // Finalize all rendering
  1099.     GdiFlush();
  1100.     // Copy the freshly rendered image to the render target
  1101.     switch( eTarget )
  1102.     {
  1103.         case DIDIRT_SURFACE:
  1104.             // Since the image is being transfered to a Direct3D surface, the stored
  1105.             // alpha information could be used.
  1106.             ApplyAlphaChannel( m_ahImages[ m_dwActiveView ], hbmpAlpha, ( (m_BkColor & ALPHA_MASK) == ALPHA_MASK ) );
  1107.             rcBitmap.right  = info.dsBm.bmWidth;
  1108.             rcBitmap.bottom = info.dsBm.bmHeight;
  1109.             hr = D3DXLoadSurfaceFromMemory( (LPDIRECT3DSURFACE9) pvTarget,
  1110.                                              NULL, NULL, info.dsBm.bmBits,
  1111.                                              D3DFMT_A8R8G8B8, 
  1112.                                              info.dsBm.bmWidthBytes,
  1113.                                              NULL, &rcBitmap, 
  1114.                                              D3DX_FILTER_NONE, 0 );
  1115.             break;
  1116.         case DIDIRT_DC:
  1117.             BitBlt( (HDC) pvTarget, 0, 0, info.dsBm.bmWidth, info.dsBm.bmHeight,
  1118.                           hdcRender, 0, 0, SRCCOPY );
  1119.             break;
  1120.     
  1121.         default:
  1122.             // Invalid render target
  1123.             hr = DIERR_INVALIDPARAM;
  1124.             goto LCleanReturn;
  1125.     }   
  1126.     
  1127. LCleanReturn:
  1128.     
  1129.     // Restore the background
  1130.     if( pSavedPixels )
  1131.         CopyMemory( info.dsBm.bmBits, pSavedPixels, info.dsBm.bmWidthBytes * info.dsBm.bmHeight );
  1132.   
  1133.     DeleteDC( hdcRender );
  1134.     DeleteDC( hdcAlpha );
  1135.     DeleteObject( hbmpAlpha );
  1136.     delete [] pSavedPixels;
  1137.     delete [] pCleanPixels;
  1138.     return hr;
  1139. }
  1140. //-----------------------------------------------------------------------------
  1141. // Name: AddObject
  1142. // Desc: Adds an object to the current list according to object ID. If an 
  1143. //       object with the given ID already exists, a pointer to it returned. 
  1144. //       Otherwise, a new object is created and a pointer to the new object
  1145. //       is returned. Returns NULL if memory couldn't be allocated.
  1146. //-----------------------------------------------------------------------------
  1147. HRESULT CDIDevImage::AddObject( DWORD dwID )
  1148. {
  1149.     CDIDIObject* pObject = NULL;
  1150.     // Search through current objects
  1151.     if( GetObject( dwID ) )
  1152.         return DI_OK;
  1153.     // Did not find object. Create a new object, and store pointer
  1154.     pObject = new CDIDIObject( dwID, m_dwNumViews );
  1155.     if( NULL == pObject )
  1156.         return DIERR_OUTOFMEMORY;
  1157.     m_apObject[m_dwNumObjects++] = pObject;
  1158.     
  1159.     return DI_OK;
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. // Name: GetObject
  1163. // Desc: If an object with given ID exist, a pointer to it is returned
  1164. //-----------------------------------------------------------------------------
  1165. CDIDIObject* CDIDevImage::GetObject( DWORD dwID )
  1166. {
  1167.     for( UINT i=0; i < m_dwNumObjects; i++ )
  1168.     {
  1169.         if( m_apObject[i]->GetID() == dwID )
  1170.             return m_apObject[i];
  1171.     }
  1172.     return NULL;
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Name: LoadImageInfo
  1176. // Desc: helper function to retrieve callout / image data from
  1177. //       DirectInput
  1178. //-----------------------------------------------------------------------------
  1179. HRESULT CDIDevImage::LoadImageInfo( LPDIRECTINPUTDEVICE8 pDIDevice )
  1180. {
  1181.     HRESULT hr;
  1182.     DWORD   dwBufferCount = 0;
  1183.     DIDEVICEIMAGEINFOHEADER dihImageHeader = {0};
  1184.     DIDEVICEIMAGEINFO *pInfo = NULL;
  1185.     // properly initialize the structure before it can be used
  1186.     dihImageHeader.dwSize = sizeof( DIDEVICEIMAGEINFOHEADER );
  1187.     dihImageHeader.dwSizeImageInfo = sizeof( DIDEVICEIMAGEINFO );
  1188.     
  1189.     // since m_dihImageHeader.dwBufferSize is 0, this call serves to determine
  1190.     // the minimum buffer size required to hold information for all the images
  1191.     hr = pDIDevice->GetImageInfo( &dihImageHeader );
  1192.     if( FAILED(hr) )
  1193.         return hr;
  1194.     // at this point, m_lpDidImgHeader->dwBufferUsed has been set by
  1195.     // the GetImageInfo method to minimum buffer size needed, so allocate.
  1196.     dihImageHeader.dwBufferSize = dihImageHeader.dwBufferUsed;
  1197.     dihImageHeader.lprgImageInfoArray = (DIDEVICEIMAGEINFO*) new BYTE[dihImageHeader.dwBufferSize];
  1198.     // make sure memory has been allocated
  1199.     if( NULL == dihImageHeader.lprgImageInfoArray )
  1200.         return DIERR_OUTOFMEMORY;
  1201.     // now that the dwBufferSize has been filled, and lprgImageArray allocated,
  1202.     // we call GetImageInfo again to get the image data
  1203.     hr = pDIDevice->GetImageInfo( &dihImageHeader );
  1204.     if( FAILED(hr) )
  1205.         goto LCleanReturn;
  1206.     
  1207.     // Allocate space for all the object/callouts/overlays
  1208.     m_apObject = new CDIDIObject* [dihImageHeader.dwcButtons + 
  1209.                                    dihImageHeader.dwcAxes +
  1210.                                    dihImageHeader.dwcPOVs];
  1211.     if( NULL == m_apObject )
  1212.     {
  1213.         hr = DIERR_OUTOFMEMORY;
  1214.         goto LCleanReturn;
  1215.     }
  1216.     m_dwNumViews = dihImageHeader.dwcViews;
  1217.     // Allocate space for background images
  1218.     m_atszImagePath = new TCHAR[m_dwNumViews][MAX_PATH];
  1219.     if( NULL == m_atszImagePath )
  1220.     {
  1221.         hr = DIERR_OUTOFMEMORY;
  1222.         goto LCleanReturn;
  1223.     }
  1224.     ZeroMemory( m_atszImagePath, sizeof(m_atszImagePath) );
  1225.     m_ahImages = new HBITMAP[m_dwNumViews];
  1226.     if( NULL == m_ahImages )
  1227.     {
  1228.         hr = DIERR_OUTOFMEMORY;
  1229.         goto LCleanReturn;
  1230.     }
  1231.     ZeroMemory( m_ahImages, sizeof(m_ahImages) );
  1232.     // Fill the data from the ImageHeader
  1233.     pInfo = dihImageHeader.lprgImageInfoArray;
  1234.     dwBufferCount = dihImageHeader.dwBufferUsed;
  1235.     while( dwBufferCount > 0)
  1236.     {
  1237.         if( pInfo->dwViewID > m_dwNumViews )
  1238.         {
  1239.             // Error in the input format, this is out of bounds for our array
  1240.             hr = E_FAIL;
  1241.             goto LCleanReturn;
  1242.         }
  1243.         if( pInfo->dwFlags & DIDIFT_CONFIGURATION )
  1244.         {
  1245.             lstrcpy( m_atszImagePath[pInfo->dwViewID], pInfo->tszImagePath );
  1246.         }
  1247.         else if( pInfo->dwFlags & DIDIFT_OVERLAY )
  1248.         {
  1249.             hr = AddObject( pInfo->dwObjID );
  1250.             if( FAILED(hr) )
  1251.                 goto LCleanReturn;
  1252.             CDIDIObject *pDIObj = GetObject( pInfo->dwObjID ); 
  1253.             if( NULL == pDIObj )
  1254.             {
  1255.                 hr = DIERR_OUTOFMEMORY;
  1256.                 goto LCleanReturn;
  1257.             }
  1258.             // Overlay
  1259.             if( pInfo->tszImagePath[0] )
  1260.                 pDIObj->SetOverlay( pInfo->dwViewID, pInfo->tszImagePath, pInfo->rcOverlay );
  1261.            
  1262.             // Callout
  1263.             pDIObj->SetCallout( pInfo->dwViewID, pInfo->dwcValidPts, pInfo->rgptCalloutLine, pInfo->rcCalloutRect, pInfo->dwTextAlign );
  1264.         }
  1265.         
  1266.         pInfo++;
  1267.         dwBufferCount -= dihImageHeader.dwSizeImageInfo;
  1268.     }
  1269.     // We made it this far, set the return value as success.
  1270.     hr = DI_OK;
  1271. LCleanReturn:
  1272.     
  1273.     // Release the resources used for the image info structure
  1274.     delete [] dihImageHeader.lprgImageInfoArray;
  1275.     return hr;
  1276. }
  1277. //-----------------------------------------------------------------------------
  1278. // Name: CreateCustomImageInfo
  1279. // Desc: Create a default UI for the given device, and fill in all neccesary
  1280. //       structures to support rendering
  1281. //-----------------------------------------------------------------------------
  1282. HRESULT CDIDevImage::CreateCustomImageInfo( LPDIRECTINPUTDEVICE8 pDIDevice )
  1283. {
  1284.     HRESULT hr;
  1285.     DIDEVCAPS didc;
  1286.     // Allocate space for all the device's objects (axes, buttons, POVS)
  1287.     ZeroMemory( &didc, sizeof(DIDEVCAPS) );
  1288.     didc.dwSize = sizeof(DIDEVCAPS);
  1289.     hr = pDIDevice->GetCapabilities( &didc );
  1290.     if( FAILED(hr) )
  1291.         return hr;
  1292.     m_apObject = new CDIDIObject* [didc.dwAxes + didc.dwButtons + didc.dwPOVs];
  1293.     if( NULL == m_apObject )
  1294.         return DIERR_OUTOFMEMORY;
  1295.     
  1296.     hr = pDIDevice->EnumObjects( EnumDeviceObjectsCB, this, DIDFT_AXIS );
  1297.     if( FAILED(hr) )
  1298.         return hr;
  1299.     hr = pDIDevice->EnumObjects( EnumDeviceObjectsCB, this, DIDFT_BUTTON );
  1300.     if( FAILED(hr))
  1301.         return hr;
  1302.     hr = pDIDevice->EnumObjects( EnumDeviceObjectsCB, this, DIDFT_POV );
  1303.     if( FAILED(hr))
  1304.         return hr;
  1305.     return DI_OK;
  1306. }
  1307. //-----------------------------------------------------------------------------
  1308. // Name: LoadImages
  1309. // Desc: Load all images associated with the active view
  1310. //-----------------------------------------------------------------------------
  1311. HRESULT CDIDevImage::LoadImages()
  1312. {
  1313.     UINT               i;
  1314.     HRESULT            hr;
  1315.     SIZE               sizeInit = {0};
  1316.     SIZE               sizeScaled = {0};
  1317.     FLOAT              fxScale, fyScale;
  1318.     D3DXIMAGE_INFO     d3dxImageInfo;
  1319.     LPDIRECT3DSURFACE9 pLoadSurface = NULL;
  1320.     LPDIRECT3DSURFACE9 pScaleSurface = NULL;
  1321.     
  1322.     // Create a temporary surface
  1323.     pLoadSurface = GetCloneSurface( DIDICONST_MAX_IMAGE_WIDTH, DIDICONST_MAX_IMAGE_HEIGHT );
  1324.     
  1325.     // Load the background image onto the temporary loading surface
  1326.     hr = D3DXLoadSurfaceFromFile( pLoadSurface, NULL, NULL, 
  1327.                                   m_atszImagePath[m_dwActiveView],
  1328.                                   NULL, D3DX_FILTER_NONE, 
  1329.                                   NULL, &d3dxImageInfo );
  1330.     if( FAILED(hr) )
  1331.         goto LCleanReturn;
  1332.    
  1333.     // The actual dimensions of the render surface are determined
  1334.     // by the background image, the overlay images, and the 
  1335.     // callout rects. 
  1336.     sizeInit.cx = d3dxImageInfo.Width;
  1337.     sizeInit.cy = d3dxImageInfo.Height;
  1338.     for( i=0; i < m_dwNumObjects; i++ )
  1339.     {
  1340.         DIDICallout* pCallout = m_apObject[i]->GetCallout( m_dwActiveView );
  1341.         
  1342.         sizeInit.cx = max( sizeInit.cx, pCallout->rcInit.right );
  1343.         sizeInit.cy = max( sizeInit.cy, pCallout->rcInit.bottom );
  1344.     }
  1345.     // Determine the scaling parameters
  1346.     switch( m_dwScaleMethod )
  1347.     {
  1348.         case( DIDISOIS_RESIZE ) :
  1349.             sizeScaled.cx = m_dwWidthPref;
  1350.             sizeScaled.cy = m_dwHeightPref;
  1351.             break;
  1352.         case( DIDISOIS_MAINTAINASPECTUSINGWIDTH ) :
  1353.             sizeScaled.cx = m_dwWidthPref;
  1354.             sizeScaled.cy = (LONG) ( 0.5 + sizeInit.cy * ( (FLOAT)m_dwWidthPref  / sizeInit.cx ) );
  1355.             break;
  1356.   
  1357.         case( DIDISOIS_MAINTAINASPECTUSINGHEIGHT ) :
  1358.             sizeScaled.cx = (LONG) ( 0.5 + sizeInit.cx * ( (FLOAT)m_dwHeightPref / sizeInit.cy ) );
  1359.             sizeScaled.cy = m_dwHeightPref;
  1360.             break;
  1361.         case( DIDISOIS_DEFAULT ) :
  1362.         default :
  1363.             sizeScaled.cx = sizeInit.cx;
  1364.             sizeScaled.cy = sizeInit.cy;
  1365.             break;  
  1366.     }
  1367.     // Calculate scaling multipliers
  1368.     fxScale = (FLOAT)sizeScaled.cx / sizeInit.cx;
  1369.     fyScale = (FLOAT)sizeScaled.cy / sizeInit.cy;
  1370.     // Scale all object display parameters
  1371.     for( i=0; i < m_dwNumObjects; i++ )
  1372.     {
  1373.         m_apObject[i]->ScaleView( m_dwActiveView, fxScale, fyScale );
  1374.     }
  1375.     // Load the background image
  1376.     hr = CreateScaledSurfaceCopy( pLoadSurface, d3dxImageInfo.Width, d3dxImageInfo.Height, 
  1377.                                   fxScale, fyScale, &pScaleSurface );
  1378.     if( FAILED(hr) )
  1379.         goto LCleanReturn;
  1380.     // Create a DIB section for the loaded image
  1381.     hr = CreateDIBSectionFromSurface( pScaleSurface, &(m_ahImages[ m_dwActiveView ]), &sizeScaled );
  1382.     if( FAILED(hr) )
  1383.         goto LCleanReturn;
  1384.     SAFE_RELEASE( pScaleSurface );
  1385.     // Apply the background color
  1386.     FillBackground( m_ahImages[ m_dwActiveView ], m_BkColor );
  1387.     // Load all object images
  1388.     for( i=0; i < m_dwNumObjects; i++ )
  1389.     {
  1390.         DIDIOverlay *pOverlay = m_apObject[i]->GetOverlay( m_dwActiveView );
  1391.         // Load the file onto the temporary surface
  1392.         if( !pOverlay->strImagePath[0] )
  1393.             continue;
  1394.         hr = D3DXLoadSurfaceFromFile( pLoadSurface, 
  1395.                                       NULL, NULL, 
  1396.                                       pOverlay->strImagePath,
  1397.                                       NULL, D3DX_FILTER_NONE, 
  1398.                                       NULL, &d3dxImageInfo );
  1399.         if( FAILED(hr) )
  1400.             continue;
  1401.         
  1402.         // Since overlay rectanges are actually defined by the image size, some
  1403.         // of the image info files simply define the top-left coordinate of the
  1404.         // rectangle. We may want good data for the overlay rectangle, so set
  1405.         // the rect based on image size
  1406.         pOverlay->rcInit.bottom = pOverlay->rcInit.top  + d3dxImageInfo.Height;
  1407.         pOverlay->rcInit.right  = pOverlay->rcInit.left + d3dxImageInfo.Width;
  1408.         ScaleRect( &( pOverlay->rcInit), &( pOverlay->rcScaled ), fxScale, fyScale );
  1409.         // Scale the overlay
  1410.         hr = CreateScaledSurfaceCopy( pLoadSurface, d3dxImageInfo.Width, d3dxImageInfo.Height,
  1411.                                       fxScale, fyScale, &pScaleSurface );
  1412.         if( FAILED(hr) )
  1413.             goto LCleanReturn;
  1414.      
  1415.         // Load the stored bitmap from the scaled D3D surface
  1416.         hr = CreateDIBSectionFromSurface( pScaleSurface, &(pOverlay->hImage) );
  1417.         if( FAILED(hr) )
  1418.             goto LCleanReturn;
  1419.         SAFE_RELEASE( pScaleSurface );
  1420.     }
  1421.     hr = DI_OK;
  1422. LCleanReturn:
  1423.     SAFE_RELEASE( pLoadSurface );
  1424.     SAFE_RELEASE( pScaleSurface );
  1425.     
  1426.     return hr;
  1427. }
  1428. //-----------------------------------------------------------------------------
  1429. // Name: GetCustomUISize
  1430. // Desc: Determine the dimensions of the custom UI based on default values and
  1431. //       user-supplied sizing information.
  1432. //-----------------------------------------------------------------------------
  1433. HRESULT CDIDevImage::GetCustomUISize( SIZE* pSize )
  1434. {
  1435.     if( pSize == NULL )
  1436.         return DIERR_INVALIDPARAM;
  1437.     // Calculate view dimensions based on values set during a call to 
  1438.     // SetOutputImageSize(), or the default values defined in the header
  1439.     switch( m_dwScaleMethod )
  1440.     {
  1441.     case DIDISOIS_RESIZE :
  1442.         pSize->cx = m_dwWidthPref;
  1443.         pSize->cy = m_dwHeightPref;
  1444.         break;
  1445.     case DIDISOIS_MAINTAINASPECTUSINGWIDTH :
  1446.         pSize->cx = m_dwWidthPref;
  1447.         pSize->cy = (LONG) ( 0.5 + DIDICONST_CUSTOM_VIEW_HEIGHT *
  1448.                                ( (FLOAT)m_dwWidthPref / DIDICONST_CUSTOM_VIEW_WIDTH ) );
  1449.         break;
  1450.     case DIDISOIS_MAINTAINASPECTUSINGHEIGHT :
  1451.         pSize->cy = m_dwHeightPref;
  1452.         pSize->cx = (LONG) ( 0.5 + DIDICONST_CUSTOM_VIEW_WIDTH *
  1453.                                ( (FLOAT)m_dwHeightPref / DIDICONST_CUSTOM_VIEW_HEIGHT ) );
  1454.         break;
  1455.     default:
  1456.         pSize->cx = DIDICONST_CUSTOM_VIEW_WIDTH;
  1457.         pSize->cy = DIDICONST_CUSTOM_VIEW_HEIGHT;
  1458.     };
  1459.     return DI_OK;
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Name: BuildCustomUI
  1463. // Desc: Creates the callout rects for each view based on stored sizing 
  1464. // information and callout strings.
  1465. //-----------------------------------------------------------------------------
  1466. HRESULT CDIDevImage::BuildCustomUI()
  1467. {
  1468.     HDC     hdc      = NULL;
  1469.     SIZE    size     = {0};
  1470.     UINT    i        = 0;
  1471.     int nMaxNameWidth     = 0;
  1472.     int nMaxNameHeight    = 0;
  1473.     int nMaxCalloutWidth  = 0;
  1474.     int nMaxCalloutHeight = 0;
  1475.     int nRowsPerView      = 0;
  1476.     int nColsPerView      = 0;
  1477.     int nNumVisObjects    = 0;
  1478.     const int GUTTER_SIZE    = 20;
  1479.     const int PADDING_SIZE   = 20;
  1480.     const int SPACING_WIDTH  = 10;
  1481.     const int SPACING_HEIGHT = 10;
  1482.     const int MAX_CHARS_OBJECT = 20;
  1483.     const int MAX_CHARS_ACTION = 20;
  1484.     // we need a device context in order to evaluate the text metrics
  1485.     hdc = CreateDC( TEXT("DISPLAY"), NULL, NULL, NULL );
  1486.     if( NULL == hdc )
  1487.         return E_FAIL;
  1488.     // select the font into the dc
  1489.     SelectObject( hdc, m_hFont );
  1490.     // determine the largest device name
  1491.     for( i=0; i < m_dwNumObjects; i++ )
  1492.     {
  1493.         if( DIDICOS_INVISIBLE & m_apObject[i]->GetCalloutState() )
  1494.             continue;
  1495.         nNumVisObjects++;
  1496.         TCHAR str[ MAX_PATH ] = {0};
  1497.        
  1498.         m_apObject[i]->GetName( str, MAX_PATH );
  1499.         if( lstrlen(str) > 15 )
  1500.             lstrcpy( &str[15], TEXT("...") );
  1501.         if( str && GetTextExtentPoint32( hdc, str, lstrlen( str ), &size ) )
  1502.         {
  1503.             nMaxNameWidth  = max( nMaxNameWidth,  size.cx );
  1504.             nMaxNameHeight = max( nMaxNameHeight, size.cy ); 
  1505.         }
  1506.         m_apObject[i]->GetCalloutText( str, MAX_PATH );
  1507.         if( lstrlen(str) > 15 )
  1508.             lstrcpy( &str[15], TEXT("...") );
  1509.         if( str && GetTextExtentPoint32( hdc, str, lstrlen( str ), &size ) )
  1510.         {
  1511.             nMaxCalloutWidth  = max( nMaxCalloutWidth,  size.cx );
  1512.             nMaxCalloutHeight = max( nMaxCalloutHeight, size.cy ); 
  1513.         }
  1514.        
  1515.     }
  1516.     
  1517.     // Optionally, you can help constrain the callout sizes by restricting the
  1518.     // string lengths.
  1519.     TEXTMETRIC tm = {0};
  1520.     if( GetTextMetrics( hdc, &tm ) )
  1521.     {
  1522.         nMaxCalloutWidth = min( nMaxCalloutWidth, MAX_CHARS_ACTION * tm.tmAveCharWidth );
  1523.         nMaxNameWidth    = min( nMaxNameWidth,    MAX_CHARS_OBJECT * tm.tmAveCharWidth );
  1524.     }
  1525.     // Release resources
  1526.     DeleteDC( hdc );
  1527.     // Calculate view dimensions
  1528.     GetCustomUISize( &size );
  1529.     // determine how many callouts we can fit on a single view
  1530.     nColsPerView = ( size.cx - (2 * PADDING_SIZE) + GUTTER_SIZE ) / 
  1531.                    ( nMaxNameWidth + nMaxCalloutWidth + SPACING_WIDTH + GUTTER_SIZE );
  1532.     nColsPerView = max( nColsPerView, 1 );
  1533.     nRowsPerView = ( size.cy - (2 * PADDING_SIZE) ) /
  1534.                    ( nMaxNameHeight + SPACING_HEIGHT );
  1535.     nRowsPerView = max( nRowsPerView, 1 );
  1536.     m_dwNumViews = nNumVisObjects / ( nColsPerView * nRowsPerView );
  1537.     m_dwNumViews = max( m_dwNumViews, 1 );
  1538.     // now all the dimensions are calculated and the callouts can be
  1539.     // allocated. 
  1540.     for( i=0; i < m_dwNumObjects; i++ )
  1541.     {
  1542.         m_apObject[i]->AllocateViews( m_dwNumViews );
  1543.     }
  1544.     DIDICallout* pCallout = NULL;
  1545.     DIDIOverlay* pOverlay = NULL;
  1546.     int x = 0, y = 0;
  1547.     UINT index = 0;
  1548.     // Build the view by enumerating through the callouts and 
  1549.     // For each view...
  1550.     for( UINT view=0; view < m_dwNumViews; view++ )
  1551.     {
  1552.         x = PADDING_SIZE;
  1553.         // For each column...
  1554.         for( int col=0; col < nColsPerView; col++ )
  1555.         {
  1556.             y = PADDING_SIZE;
  1557.             // For each row...
  1558.             for( int row=0; row < nRowsPerView; row++ )
  1559.             {          
  1560.                 // Eat indices to invisible objects until an object is found or
  1561.                 // we run out.
  1562.                 while( index < m_dwNumObjects && 
  1563.                        ( DIDICOS_INVISIBLE & m_apObject[index]->GetCalloutState() ) )
  1564.                 {
  1565.                     index++;
  1566.                 }
  1567.                 // If we're on a valid object, calculate the screen coords
  1568.                 if( index < m_dwNumObjects )
  1569.                 {
  1570.                     pOverlay = m_apObject[index]->GetOverlay( view );
  1571.                     pCallout = m_apObject[index]->GetCallout( view );
  1572.                     pOverlay->rcScaled.left = x;
  1573.                     pOverlay->rcScaled.top  = y;
  1574.                     pOverlay->rcScaled.right = x + nMaxNameWidth;
  1575.                     pOverlay->rcScaled.bottom = y + nMaxNameHeight;
  1576.                     pCallout->rcScaled.left = x + nMaxNameWidth + SPACING_WIDTH;
  1577.                     pCallout->rcScaled.top = y;
  1578.                     pCallout->rcScaled.right = pCallout->rcScaled.left + nMaxCalloutWidth;
  1579.                     pCallout->rcScaled.bottom = y + nMaxNameHeight;
  1580.                     pCallout->dwTextAlign = DIDAL_LEFTALIGNED | DIDAL_BOTTOMALIGNED;
  1581.                 }
  1582.                 index++;
  1583.              
  1584.                 y += nMaxNameHeight + SPACING_HEIGHT;
  1585.             }
  1586.             x += nMaxNameWidth + nMaxCalloutWidth + SPACING_WIDTH + GUTTER_SIZE;
  1587.         }
  1588.     }
  1589.     // Clear the invalid flag
  1590.     m_bInvalidUI = FALSE;  
  1591.     return DI_OK;
  1592. }
  1593. //-----------------------------------------------------------------------------
  1594. // Name: CreateScaledSurfaceCopy
  1595. // Desc: Creates a new surface and copies the contents from the provided
  1596. //       source surface onto the newly created destination surface.
  1597. //-----------------------------------------------------------------------------
  1598. HRESULT CDIDevImage::CreateScaledSurfaceCopy( LPDIRECT3DSURFACE9 pSurfaceSrc,
  1599.                                               DWORD dwWidthSrc, DWORD dwHeightSrc, 
  1600.                                               FLOAT fxScale, FLOAT fyScale, 
  1601.                                               LPDIRECT3DSURFACE9 *ppSurfaceDest )
  1602. {
  1603.     HRESULT hr;
  1604.     RECT    rcSrc = {0}, rcDest = {0}; 
  1605.     // Verify parameters
  1606.     if( NULL == pSurfaceSrc || NULL == ppSurfaceDest )
  1607.         return DIERR_INVALIDPARAM;
  1608.     // Calculate creation arguments
  1609.     rcSrc.right  = dwWidthSrc-1; 
  1610.     rcSrc.bottom = dwHeightSrc-1;
  1611.     ScaleRect( &rcSrc, &rcDest, fxScale, fyScale );
  1612.     
  1613.     // Create the stored surface
  1614.     *ppSurfaceDest = GetCloneSurface( rcDest.right, rcDest.bottom );
  1615.                   
  1616.     // Since we're using a surface workaround, the d3d functions should only
  1617.     // be called if we're actually scaling the surface.
  1618.     if( EqualRect( &rcSrc, &rcDest ) )
  1619.     {
  1620.         D3DLOCKED_RECT d3drcSrc = {0};
  1621.         D3DLOCKED_RECT d3drcDest = {0};
  1622.         pSurfaceSrc->LockRect( &d3drcSrc, NULL, 0 );
  1623.         (*ppSurfaceDest)->LockRect( &d3drcDest, NULL, 0 );
  1624.         BYTE* pBitsSrc = (BYTE*) d3drcSrc.pBits;
  1625.         BYTE* pBitsDest = (BYTE*) d3drcDest.pBits;
  1626.         for( int y = rcDest.top; y < rcDest.bottom; y++ )
  1627.         {
  1628.             CopyMemory( pBitsDest, pBitsSrc, d3drcDest.Pitch );
  1629.             pBitsDest += d3drcDest.Pitch;
  1630.             pBitsSrc += d3drcSrc.Pitch;
  1631.         }
  1632.     }
  1633.     else
  1634.     {
  1635.         // Copy the image onto the stored surface
  1636.         hr = D3DXLoadSurfaceFromSurface( *ppSurfaceDest, 
  1637.                                           NULL, &rcDest, 
  1638.                                           pSurfaceSrc, NULL,
  1639.                                           &rcSrc, D3DX_FILTER_TRIANGLE,
  1640.                                           NULL );
  1641.         if( FAILED(hr) )
  1642.             goto LCleanReturn;
  1643.     }
  1644.     // Everything went OK. Return before cleaning up the new surface.
  1645.     return DI_OK;
  1646. LCleanReturn:
  1647.     // An error occured. Clean up the new surface before returning.
  1648.     if( *ppSurfaceDest )
  1649.     {
  1650.        (*ppSurfaceDest)->Release();
  1651.         *ppSurfaceDest = NULL;
  1652.     }
  1653.     
  1654.     return hr;
  1655. }
  1656. //-----------------------------------------------------------------------------
  1657. // Name: CleanUp
  1658. // Desc: Release all allocated memory, zero out member variables
  1659. //-----------------------------------------------------------------------------
  1660. void CDIDevImage::CleanUp()
  1661. {
  1662.     UINT i=0; //loop var
  1663.     // Release resources first
  1664.     DestroyImages();
  1665.     // Call destructors on all stored objects
  1666.     for( i=0; i < m_dwNumObjects; i++ )
  1667.     {
  1668.         delete m_apObject[i];
  1669.     }
  1670.     // Delete object array
  1671.     if( m_apObject )
  1672.         delete [] m_apObject;
  1673.     // Delete BITMAP storage array
  1674.     delete [] m_atszImagePath;
  1675.     delete [] m_ahImages;
  1676.     if( m_hFont )
  1677.         DeleteObject( m_hFont );
  1678.     m_apObject      = NULL;
  1679.     m_atszImagePath = NULL;
  1680.     m_ahImages      = NULL;
  1681.     m_dwNumObjects  = 0;
  1682.     m_dwNumViews    = 0;
  1683.     m_dwWidthPref   = 0;
  1684.     m_dwHeightPref  = 0;
  1685.     m_dwScaleMethod = 0;
  1686.     m_hFont         = NULL;
  1687.     m_bInitialized  = FALSE;
  1688.     m_bCustomUI     = FALSE;
  1689.     m_bInvalidUI    = TRUE;
  1690. }
  1691. //-----------------------------------------------------------------------------
  1692. // Name: DestroyImages
  1693. // Desc: Release all stored images
  1694. //-----------------------------------------------------------------------------
  1695. VOID CDIDevImage::DestroyImages()
  1696. {
  1697.     UINT i=0; //loop var
  1698.     // Release all background images
  1699.     if( m_ahImages )
  1700.     {
  1701.         for( i=0; i < m_dwNumViews; i++ )
  1702.         {
  1703.             if( m_ahImages[i] )
  1704.                 DeleteObject( m_ahImages[i] );
  1705.             m_ahImages[i] = NULL;
  1706.         }
  1707.     }
  1708.     // Release all object images
  1709.     for( i=0; i < m_dwNumObjects; i++ )
  1710.     {
  1711.         if( m_apObject[i] )
  1712.             m_apObject[i]->DestroyImages();
  1713.     }
  1714. };
  1715. //-----------------------------------------------------------------------------
  1716. // Name: CreateFont
  1717. // Desc: Create the GDI font to use for callout text
  1718. //-----------------------------------------------------------------------------
  1719. VOID CDIDevImage::CreateFont()
  1720. {
  1721.     // Create display font
  1722.     LOGFONT lf;
  1723.     ZeroMemory( &lf, sizeof(LOGFONT) );
  1724.     lf.lfHeight = 14;
  1725.     lf.lfWeight = 700;
  1726.     lf.lfCharSet = DEFAULT_CHARSET;
  1727.     lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  1728.     _tcscpy( lf.lfFaceName, TEXT("arial") );
  1729.     m_hFont = CreateFontIndirect( &lf );
  1730.     // Have a backup plan
  1731.     if( NULL == m_hFont )
  1732.         m_hFont = (HFONT) GetStockObject( DEFAULT_GUI_FONT );
  1733. }
  1734. //-----------------------------------------------------------------------------
  1735. // Name: CDIDIObject
  1736. // Desc: Constructor
  1737. //-----------------------------------------------------------------------------
  1738. CDIDIObject::CDIDIObject( DWORD dwID, DWORD dwNumViews )
  1739. {
  1740.     m_dwID        = dwID; 
  1741.     m_crNormColor = RGB(150, 150, 200);
  1742.     m_crHighColor = RGB(255, 255, 255);
  1743.     m_dwNumViews  = dwNumViews;
  1744.     m_aCallout = NULL;
  1745.     m_aOverlay = NULL;
  1746.     m_dwState  = 0;
  1747.     AllocateViews( dwNumViews );
  1748.     lstrcpy( m_strCallout, TEXT("_ _ _") );
  1749.     lstrcpy( m_strName,    TEXT("") );
  1750. };
  1751. //-----------------------------------------------------------------------------
  1752. // Name: ~CDIDIObject
  1753. // Desc: Destructor
  1754. //-----------------------------------------------------------------------------
  1755. CDIDIObject::~CDIDIObject() 
  1756. {
  1757.     DestroyImages();
  1758.     delete [] m_aCallout;
  1759.     delete [] m_aOverlay;
  1760. };
  1761. //-----------------------------------------------------------------------------
  1762. // Name: AllocateViews
  1763. // Desc: The number of views for a custom UI can change, requiring the need
  1764. //       to free and reallocate resources depending on the number of views.
  1765. //-----------------------------------------------------------------------------
  1766. HRESULT CDIDIObject::AllocateViews( DWORD dwNumViews ) 
  1767. {
  1768.     // Release current views
  1769.     delete [] m_aCallout;
  1770.     delete [] m_aOverlay;
  1771.     m_dwNumViews = 0;
  1772.     m_aCallout = new DIDICallout[dwNumViews];
  1773.     if( NULL == m_aCallout )
  1774.         return DIERR_OUTOFMEMORY;
  1775.     m_aOverlay = new DIDIOverlay[dwNumViews];
  1776.     if( NULL == m_aOverlay )
  1777.     {
  1778.         delete[] m_aCallout;
  1779.         m_aCallout = NULL;
  1780.         return DIERR_OUTOFMEMORY;
  1781.     }
  1782.     ZeroMemory( m_aCallout, sizeof(DIDICallout) * dwNumViews );
  1783.     ZeroMemory( m_aOverlay, sizeof(DIDIOverlay) * dwNumViews );
  1784.     m_dwNumViews = dwNumViews;
  1785.     return DI_OK;
  1786. };
  1787. //-----------------------------------------------------------------------------
  1788. // Name: SetOverlay
  1789. // Desc: Sets the values for an overlay in the object's current list
  1790. //-----------------------------------------------------------------------------
  1791. VOID CDIDIObject::SetOverlay( DWORD dwViewID, LPCTSTR strImagePath, RECT rect )
  1792. {
  1793.     m_aOverlay[dwViewID].rcInit = rect;
  1794.     m_aOverlay[dwViewID].rcScaled = rect;
  1795.     lstrcpy( m_aOverlay[dwViewID].strImagePath, strImagePath );
  1796. }
  1797. //-----------------------------------------------------------------------------
  1798. // Name: SetCallout
  1799. // Desc: Adds the values for a callout in the object's current list
  1800. //-----------------------------------------------------------------------------
  1801. VOID CDIDIObject::SetCallout( DWORD dwViewID, DWORD dwNumPoints, POINT *aptLine, RECT rect, DWORD dwTextAlign )
  1802. {
  1803.     m_aCallout[dwViewID].dwNumPoints = dwNumPoints;
  1804.     m_aCallout[dwViewID].rcInit = rect;
  1805.     m_aCallout[dwViewID].rcScaled = rect;
  1806.     m_aCallout[dwViewID].dwTextAlign = dwTextAlign;
  1807.     for( int i=0; i < 5; i++)
  1808.     {
  1809.         m_aCallout[dwViewID].aptLineInit[i] = aptLine[i];
  1810.         m_aCallout[dwViewID].aptLineScaled[i] = aptLine[i];
  1811.     }
  1812. }
  1813. //-----------------------------------------------------------------------------
  1814. // Name: DestroyImages
  1815. // Desc: Release all stored images
  1816. //-----------------------------------------------------------------------------
  1817. VOID CDIDIObject::DestroyImages()
  1818. {
  1819.     UINT i=0; //loop var
  1820.     // Release all object images
  1821.     for( i=0; i < m_dwNumViews; i++ )
  1822.     {
  1823.         if( m_aOverlay[i].hImage )
  1824.             DeleteObject( m_aOverlay[i].hImage );
  1825.         m_aOverlay[i].hImage = NULL;
  1826.     }
  1827. };
  1828. //-----------------------------------------------------------------------------
  1829. // Name: SetCalloutText
  1830. // Desc: copy as many characters as will fit, leaving room for the terminating
  1831. //       null and 3 elipses.
  1832. //-----------------------------------------------------------------------------
  1833. VOID CDIDIObject::SetCalloutText( LPCTSTR strText )
  1834. {
  1835.     _tcsncpy( m_strCallout, strText, MAX_PATH - 4 );
  1836. }
  1837. //-----------------------------------------------------------------------------
  1838. // Name: GetCalloutText
  1839. // Desc: Retrieve the current callout text from the name buffer up to the 
  1840. //       amount of characters specified by dwSize
  1841. //-----------------------------------------------------------------------------
  1842. VOID CDIDIObject::GetCalloutText( LPTSTR strText, DWORD dwSize )
  1843. {
  1844.     _tcsncpy( strText, m_strCallout, dwSize );
  1845. }
  1846. //-----------------------------------------------------------------------------
  1847. // Name: ScaleView
  1848. // Desc: Scale all rects and points to the given scaling factors
  1849. //-----------------------------------------------------------------------------
  1850. VOID CDIDIObject::ScaleView( DWORD dwViewID, FLOAT fxScale, FLOAT fyScale )
  1851. {
  1852.     UINT i=0;
  1853.     // Overlay/Callout Rects
  1854.     ScaleRect( &(m_aOverlay[dwViewID].rcInit), 
  1855.                &(m_aOverlay[dwViewID].rcScaled),
  1856.                fxScale, fyScale );
  1857.     ScaleRect( &(m_aCallout[dwViewID].rcInit), 
  1858.                &(m_aCallout[dwViewID].rcScaled),
  1859.                fxScale, fyScale );
  1860.     // Callout Lines
  1861.     for( i=0; i < 5; i++ )
  1862.     {
  1863.         ScalePoint( &(m_aCallout[dwViewID].aptLineInit[i]), 
  1864.                     &(m_aCallout[dwViewID].aptLineScaled[i]),
  1865.                     fxScale, fyScale );
  1866.     }
  1867. }
  1868. //-----------------------------------------------------------------------------
  1869. // Name: ApplyAlphaChannel
  1870. // Desc: Restore the alpha information from the source bitmap to the
  1871. //       destination bitmap. GDI uses COLORREF structures which lack an alpha
  1872. //       channel, and draws everything as fully transparent. By using a 
  1873. //       separate bitmap to store opacity information, the alpha channel can
  1874. //       be restored.
  1875. //-----------------------------------------------------------------------------
  1876. HRESULT ApplyAlphaChannel( HBITMAP hbmpDest, HBITMAP hbmpSrc, BOOL bOpaque )
  1877. {
  1878.     HRESULT        hr          = S_OK;
  1879.     BITMAP         bmpInfoSrc  = {0}; 
  1880.     BITMAP         bmpInfoDest = {0};
  1881.     
  1882.    
  1883.     // Verify parameters
  1884.     if( NULL == hbmpDest || NULL == hbmpSrc )
  1885.         return DIERR_INVALIDPARAM;
  1886.     // Get the bitmap pixel data
  1887.     if( 0 == GetObject( hbmpSrc,  sizeof(BITMAP), &bmpInfoSrc ) )
  1888.         return E_FAIL;
  1889.     if( 0 == GetObject( hbmpDest, sizeof(BITMAP), &bmpInfoDest ) )
  1890.         return E_FAIL;
  1891.     // Cast the data pointers
  1892.     DWORD* pBitsSrc  = (DWORD*) bmpInfoSrc.bmBits;
  1893.     DWORD* pBitsDest = (DWORD*) bmpInfoDest.bmBits;
  1894.     // Syncronize bitmap pixel info
  1895.     GdiFlush();
  1896.     // For each pixel in the rect...
  1897.     for( int y=0; y < bmpInfoDest.bmHeight; y++ )
  1898.     {
  1899.         for( int x=0; x < bmpInfoDest.bmWidth; x++ )
  1900.         {
  1901.             if( *pBitsSrc )
  1902.             {
  1903.                 // The source bitmap contains information at this pixel, which 
  1904.                 // may signal full or partial opacity. If the passed bOpaque
  1905.                 // flag is set, the method should treat partially opaque pixels
  1906.                 // as being fully opaque. This is used when the user has selected
  1907.                 // a fully opaque background color and wants the resulting image
  1908.                 // to also be fully opaque.
  1909.                 *pBitsDest |= bOpaque ? ALPHA_MASK : ( GetBlue( *pBitsSrc ) << 24 );
  1910.             }  
  1911.             
  1912.             // Advance the pixel pointers.
  1913.             pBitsSrc++;
  1914.             pBitsDest++;
  1915.         }
  1916.     }
  1917.     return hr;
  1918. }
  1919. //-----------------------------------------------------------------------------
  1920. // Name: RestoreRect
  1921. // Desc: Restore the pixel data of an image for the given RECT
  1922. //-----------------------------------------------------------------------------
  1923. HRESULT RestoreRect( HBITMAP hbmpDest, CONST RECT* prcDest, LPBYTE pSrcPixels )
  1924. {
  1925.     BITMAP         bmpInfoDest = {0};
  1926.     // Verify parameters
  1927.     if( NULL == hbmpDest || NULL == pSrcPixels || NULL == prcDest )
  1928.         return DIERR_INVALIDPARAM;
  1929.     // Get the bitmap pixel data
  1930.     if( 0 == GetObject( hbmpDest, sizeof(BITMAP), &bmpInfoDest ) )
  1931.         return E_FAIL;
  1932.     // Cast the data pointers
  1933.     DWORD* pBitsSrc  = (DWORD*) pSrcPixels;
  1934.     DWORD* pBitsDest = (DWORD*) bmpInfoDest.bmBits;
  1935.     // Syncronize bitmap pixel info
  1936.     GdiFlush();
  1937.     // Advance the pixel pointers to the starting position
  1938.     pBitsDest += ( prcDest->top * bmpInfoDest.bmWidth ) + prcDest->left;
  1939.     pBitsSrc  += ( prcDest->top * bmpInfoDest.bmWidth ) + prcDest->left;
  1940.     // For each scanline in rcDest...
  1941.     for( int y = prcDest->top; y < prcDest->bottom; y++ )
  1942.     {
  1943.         CopyMemory( pBitsDest, pBitsSrc, sizeof(DWORD) * ( prcDest->right - prcDest->left ) );
  1944.         
  1945.         // Advance pointers
  1946.         pBitsDest += bmpInfoDest.bmWidth;
  1947.         pBitsSrc  += bmpInfoDest.bmWidth;
  1948.     
  1949.     }
  1950.     return DI_OK;
  1951. }
  1952. //-----------------------------------------------------------------------------
  1953. // Name: ApplyOverlay
  1954. // Desc: Use manual alpha blending to paste the overlay bitmap on top of the 
  1955. //       destination bitmap.
  1956. //-----------------------------------------------------------------------------
  1957. HRESULT ApplyOverlay( HBITMAP hbmpDest, CONST RECT* prcDest, HBITMAP hbmpSrc )
  1958. {
  1959.     BITMAP         bmpInfoSrc  = {0}; 
  1960.     BITMAP         bmpInfoDest = {0};
  1961.     // Verify parameters
  1962.     if( NULL == hbmpDest || NULL == hbmpSrc || NULL == prcDest )
  1963.         return DIERR_INVALIDPARAM;
  1964.     // Get the bitmap pixel data
  1965.     if( 0 == GetObject( hbmpSrc,  sizeof(BITMAP), &bmpInfoSrc ) )
  1966.         return E_FAIL;
  1967.     if( 0 == GetObject( hbmpDest, sizeof(BITMAP), &bmpInfoDest ) )
  1968.         return E_FAIL;
  1969.     // Cast the data pointers
  1970.     DWORD* pBitsSrc  = (DWORD*) bmpInfoSrc.bmBits;
  1971.     DWORD* pBitsDest = (DWORD*) bmpInfoDest.bmBits;
  1972.     // Syncronize bitmap pixel info
  1973.     GdiFlush();
  1974.     // Advance the destination pixel to the starting position
  1975.     pBitsDest += ( prcDest->top * bmpInfoDest.bmWidth ) + prcDest->left;
  1976.     // For each pixel...
  1977.     for( int y=0; y < bmpInfoSrc.bmHeight; y++ )
  1978.     {
  1979.         for( int x=0; x < bmpInfoSrc.bmWidth; x++ )
  1980.         {
  1981.             // calculate src and dest alpha
  1982.             if( ( *pBitsSrc & ALPHA_MASK ) == ALPHA_MASK )
  1983.             {
  1984.                 // Source pixel is completely opaque, have it replace the
  1985.                 // current destination pixel
  1986.                 *pBitsDest = *pBitsSrc;
  1987.             }            
  1988.             else if( ( *pBitsSrc & ALPHA_MASK ) == 0 )
  1989.             {
  1990.                 // Source pixel is completely transparent, do nothing
  1991.             }
  1992.             else
  1993.             {
  1994.             // This formula computes the blended component value:
  1995.                 // ( ALPHA * ( srcPixel - destPixel ) ) / 256 + destPixel
  1996.                 
  1997.                 DWORD dwMultiplier  = GetAlpha( *pBitsSrc );
  1998.                 // Decompose the image into color components
  1999.                 DWORD dwRed   = GetRed( *pBitsDest );
  2000.                 DWORD dwGreen = GetGreen( *pBitsDest );
  2001.                 DWORD dwBlue  = GetBlue( *pBitsDest );
  2002.                 // Calculate the component blend
  2003.                 dwRed   = ( dwMultiplier * (   GetRed( *pBitsSrc ) - dwRed   ) ) / 256 + dwRed;
  2004.                 dwGreen = ( dwMultiplier * ( GetGreen( *pBitsSrc ) - dwGreen ) ) / 256 + dwGreen;
  2005.                 dwBlue  = ( dwMultiplier * (  GetBlue( *pBitsSrc ) - dwBlue  ) ) / 256 + dwBlue;
  2006.                 
  2007.                 // Compose the blended pixel. The destination bitmap's original alpha
  2008.                 // value is preserved.
  2009.                 *pBitsDest = (*pBitsDest & ALPHA_MASK) | dwRed << 16 | dwGreen << 8 | dwBlue;
  2010.             }
  2011.             // Move to next pixel
  2012.             pBitsDest++;
  2013.             pBitsSrc++;
  2014.         }
  2015.         // Advance destination pointer
  2016.         pBitsDest += ( bmpInfoDest.bmWidth - bmpInfoSrc.bmWidth );
  2017.     }
  2018.     return DI_OK;
  2019. }
  2020. //-----------------------------------------------------------------------------
  2021. // Name: FillBackground
  2022. // Desc: Fills the background of the given bitmap with the provided fill color.
  2023. //       The background of the image is defined by the alpha channel.
  2024. //-----------------------------------------------------------------------------
  2025. HRESULT FillBackground( HBITMAP hbmpDest, D3DCOLOR Fill )
  2026. {
  2027.     BITMAP         bmpInfo     = {0}; 
  2028.     
  2029.     // Verify parameters
  2030.     if( NULL == hbmpDest )
  2031.         return DIERR_INVALIDPARAM;
  2032.     // Get the bitmap pixel data
  2033.     if( 0 == GetObject( hbmpDest, sizeof(BITMAP), &bmpInfo ) )
  2034.         return E_FAIL;
  2035.     // Cast the data pointers
  2036.     DWORD* pBits = (DWORD*) bmpInfo.bmBits;
  2037.     // Extract the component channels of the background color.
  2038.     DWORD dwBkAlpha = GetAlpha( Fill );
  2039.     DWORD dwBkRed   = GetRed( Fill );
  2040.     DWORD dwBkGreen = GetGreen( Fill );
  2041.     DWORD dwBkBlue  = GetBlue( Fill );
  2042.     // If the background color is defined as being completely opaque, the fill
  2043.     // method is performed a little differently. Normally, the per-pixel alpha
  2044.     // value is blended along with the color components, but this can result in
  2045.     // areas of the image which are partially transparent. If the provided fill
  2046.     // color is opaque, it's assumed that the user wishes to have the final
  2047.     // rendered surface completely opaque. If the provided background color is
  2048.     // partially transparent, then the alpha channel is blended to allow for
  2049.     // an antialiased border around the device image.
  2050.     BOOL  bBkOpaque = ( dwBkAlpha == 255 );
  2051.     // Syncronize bitmap pixel info
  2052.     GdiFlush();
  2053.     // For each pixel in the rect...
  2054.     for( int y=0; y < bmpInfo.bmHeight; y++ )
  2055.     {
  2056.         for( int x=0; x < bmpInfo.bmWidth; x++ )
  2057.         {
  2058.             if( (*pBits & ALPHA_MASK) == ALPHA_MASK )
  2059.             {
  2060.                 // Destination pixel is completely opaque, no fill needed.
  2061.             }            
  2062.             else if( (*pBits & ALPHA_MASK) == 0 )
  2063.             {
  2064.                 // Destination pixel is completely transparent, replace with
  2065.                 // the fill color.
  2066.                 *pBits = Fill;
  2067.             }
  2068.             else
  2069.             {
  2070.                 // This formula computes the blended component value:
  2071.                 // ( ALPHA * ( srcPixel - destPixel ) ) / 256 + destPixel
  2072.                 
  2073.                 DWORD dwMultiplier  = GetAlpha( *pBits );
  2074.                 // Calculate the component blend
  2075.                 DWORD dwAlpha = bBkOpaque ? 255 : 
  2076.                                 ( dwMultiplier * ( GetAlpha( *pBits ) - dwBkAlpha ) ) / 256 + dwBkAlpha;
  2077.                 DWORD dwRed   = ( dwMultiplier * (   GetRed( *pBits ) - dwBkRed   ) ) / 256 + dwBkRed;
  2078.                 DWORD dwGreen = ( dwMultiplier * ( GetGreen( *pBits ) - dwBkGreen ) ) / 256 + dwBkGreen;
  2079.                 DWORD dwBlue  = ( dwMultiplier * (  GetBlue( *pBits ) - dwBkBlue  ) ) / 256 + dwBkBlue;
  2080.                 
  2081.                 // Compose the final pixel color value
  2082.                 *pBits = dwAlpha << 24 | dwRed << 16 | dwGreen << 8 | dwBlue;
  2083.             }
  2084.             // Move to next pixel
  2085.             pBits++;
  2086.         }
  2087.     }
  2088.     return DI_OK;
  2089. }
  2090. //-----------------------------------------------------------------------------
  2091. // Name: DrawTooltip
  2092. // Desc: Draws tooltip text on the provided device contexts. The tooltip string
  2093. //       is drawn in relation to the RECT containing the truncated text, and 
  2094. //       using the given foreground, background, and border colors.
  2095. //-----------------------------------------------------------------------------
  2096. HRESULT DrawTooltip( HDC hdcRender, HDC hdcAlpha, LPCTSTR strTooltip, RECT* prcBitmap, 
  2097.                      RECT* prcTruncated, COLORREF crFore, COLORREF crBack, COLORREF crBorder )
  2098. {
  2099.     // Create the tooltip font. This should be highly readable at a small point
  2100.     // size, so a raster font has been chosen.
  2101.     HFONT hTipFont = NULL;
  2102.     LOGFONT lfTipFont = {0};
  2103.     SIZE sizeText = {0};
  2104.     
  2105.     lstrcpy( lfTipFont.lfFaceName, TEXT("MS Sans Serif") );
  2106.     lfTipFont.lfHeight = 8;
  2107.     lfTipFont.lfOutPrecision = OUT_RASTER_PRECIS;
  2108.     lfTipFont.lfQuality = PROOF_QUALITY;
  2109.     hTipFont = CreateFontIndirect( &lfTipFont );
  2110.     if( !hTipFont )
  2111.         hTipFont = (HFONT) GetStockObject( SYSTEM_FONT );
  2112.  
  2113.     HFONT hOldRenderFont = (HFONT) SelectObject( hdcRender, hTipFont );
  2114.     HFONT hOldAlphaFont = (HFONT) SelectObject( hdcAlpha, hTipFont );
  2115.     // How much screen space are we going to need?
  2116.     GetTextExtentPoint32( hdcRender, strTooltip, lstrlen( strTooltip ), &sizeText );
  2117.     // Position the rect right above the callout space
  2118.     RECT rcTooltip = { 0, 0, sizeText.cx, sizeText.cy };
  2119.     OffsetRect( &rcTooltip, prcTruncated->left + 2, prcTruncated->top - sizeText.cy - 2);
  2120.     InflateRect( &rcTooltip, 2, 2 );
  2121.     // Adjust the tooltip rect if it's beyond the screen edge
  2122.     if( rcTooltip.top < 0 )
  2123.         OffsetRect( &rcTooltip, 0, -rcTooltip.top );
  2124.     if( rcTooltip.right > prcBitmap->right )
  2125.         OffsetRect( &rcTooltip, prcBitmap->right - rcTooltip.right, 0 );
  2126.     // Draw tooltip 
  2127.     HBRUSH hOldRenderBrush = (HBRUSH) SelectObject( hdcRender, CreateSolidBrush( crBack ) );
  2128.     HPEN hOldRenderPen = (HPEN) SelectObject( hdcRender, CreatePen( PS_SOLID, 1, crBorder ) );
  2129.     COLORREF crOldRenderTextColor = SetTextColor( hdcRender, crFore );
  2130.     
  2131.     Rectangle( hdcRender, rcTooltip.left, rcTooltip.top, rcTooltip.right, rcTooltip.bottom );
  2132.     FillRect( hdcAlpha, &rcTooltip, (HBRUSH) GetStockObject( WHITE_BRUSH ) );
  2133.     InflateRect( &rcTooltip, -2, -2 );
  2134.     DrawText( hdcRender, strTooltip, lstrlen( strTooltip ), &rcTooltip, 0 );
  2135.     
  2136.     
  2137.     // Restore the DC state
  2138.     SelectObject( hdcRender, hOldRenderFont );
  2139.     SelectObject( hdcAlpha,  hOldAlphaFont );
  2140.     
  2141.     DeleteObject( (HBRUSH) SelectObject( hdcRender, hOldRenderBrush ) );
  2142.     DeleteObject( (HPEN)   SelectObject( hdcRender, hOldRenderPen ) );
  2143.     SetTextColor( hdcRender, crOldRenderTextColor );
  2144.     DeleteObject( hTipFont );
  2145.     return DI_OK;
  2146. }
  2147. //-----------------------------------------------------------------------------
  2148. // Name: CreateDIBSectionFromSurface
  2149. // Desc: Extract the pixel information from the provided surface and create
  2150. //       a new DIB Section.
  2151. //-----------------------------------------------------------------------------
  2152. HRESULT CreateDIBSectionFromSurface( LPDIRECT3DSURFACE9 pSurface, HBITMAP* phBitmap, SIZE* pSize )
  2153. {
  2154.     HRESULT         hr;
  2155.     D3DSURFACE_DESC d3dDesc;
  2156.     D3DLOCKED_RECT  d3dRect;
  2157.     LPBYTE          pDIBBits;
  2158.     LPBYTE          pSurfBits;
  2159.     BITMAPINFO      bmi = {0};
  2160.     // Get the surface info
  2161.     hr = pSurface->GetDesc( &d3dDesc );
  2162.     if( FAILED( hr ) )
  2163.         return hr;
  2164.     // Lock the surface
  2165.     hr = pSurface->LockRect( &d3dRect, NULL, 0 );
  2166.     if( FAILED( hr ) )
  2167.         return hr;
  2168.     pSurfBits = (LPBYTE) d3dRect.pBits;
  2169.     // Fill in the bitmap creation info
  2170.     bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2171.     bmi.bmiHeader.biWidth = pSize ? pSize->cx : d3dDesc.Width;
  2172.     bmi.bmiHeader.biHeight = pSize ? -pSize->cy : ( - (int) d3dDesc.Height ); // Top-down DIB
  2173.     bmi.bmiHeader.biPlanes = 1;
  2174.     bmi.bmiHeader.biBitCount = 32;
  2175.     bmi.bmiHeader.biCompression = BI_RGB;
  2176.     
  2177.     HDC hdcMem = CreateCompatibleDC( NULL );
  2178.     if( NULL == hdcMem )
  2179.         return DIERR_OUTOFMEMORY;
  2180.     // Create the DIBSection
  2181.     *phBitmap = CreateDIBSection( hdcMem, &bmi, DIB_RGB_COLORS,( LPVOID* )&pDIBBits, NULL, 0 );
  2182.     DeleteDC( hdcMem );
  2183.     if( NULL == *phBitmap )
  2184.          return DIERR_OUTOFMEMORY;
  2185.     
  2186.     // Copy the bits
  2187.     for( UINT y=0; y < d3dDesc.Height; y++ )
  2188.     {
  2189.         CopyMemory( pDIBBits, pSurfBits, ( d3dDesc.Width * sizeof(DWORD) ) );
  2190.         pDIBBits += bmi.bmiHeader.biWidth * sizeof(DWORD);
  2191.         pSurfBits += d3dRect.Pitch;
  2192.     }
  2193.     return DI_OK;
  2194. }
  2195. //-----------------------------------------------------------------------------
  2196. // Name: EnumDeviceObjectsCB
  2197. // Desc: Cycle through device objects, adding information to the passed 
  2198. //       CDIDevImage object when appropriate.
  2199. //-----------------------------------------------------------------------------
  2200. BOOL CALLBACK EnumDeviceObjectsCB( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef )
  2201. {
  2202.     HRESULT hr;
  2203.     // Extract the passed pointer
  2204.     CDIDevImage *pDIDevImage = (CDIDevImage*) pvRef;
  2205.     CDIDIObject *pObject     = NULL;
  2206.     // Add the object to the list
  2207.     hr = pDIDevImage->AddObject( lpddoi->dwType );
  2208.     if( FAILED(hr) )
  2209.         return DIENUM_STOP;
  2210.     // Set the object's friendly name
  2211.     pObject = pDIDevImage->GetObject( lpddoi->dwType );
  2212.     if( pObject )
  2213.         pObject->SetName( lpddoi->tszName );
  2214.     return DIENUM_CONTINUE;
  2215. }
  2216. //-----------------------------------------------------------------------------
  2217. // Name: DidcvDirect3DSurface9Clone
  2218. // Desc: a clone surface
  2219. //-----------------------------------------------------------------------------
  2220. class DidcvDirect3DSurface9Clone : public IUnknown
  2221. {
  2222. private:
  2223.     int m_iRefCount;
  2224.     BYTE *m_pData;
  2225.     D3DSURFACE_DESC m_Desc;
  2226. public:
  2227.     DidcvDirect3DSurface9Clone() : m_pData( NULL ), m_iRefCount( 1 ) { }
  2228.     ~DidcvDirect3DSurface9Clone() { delete[] m_pData; }
  2229. public:     
  2230.     // IUnknown methods
  2231.     STDMETHOD( QueryInterface )( REFIID  riid, VOID  **ppvObj ) { return E_NOINTERFACE; }
  2232.     STDMETHOD_( ULONG,AddRef )() { return ++m_iRefCount; }
  2233.     STDMETHOD_( ULONG,Release )()
  2234.     {
  2235.         if( !--m_iRefCount )
  2236.         {
  2237.             delete this;
  2238.             return 0;
  2239.         }
  2240.         return m_iRefCount;
  2241.     }
  2242.     // IDirect3DResource9 methods
  2243.     STDMETHOD( GetDevice )( IDirect3DDevice9 **ppDevice ) { return E_FAIL; }
  2244.     STDMETHOD( SetPrivateData )( REFGUID riid, CONST VOID *pvData, DWORD cbData, DWORD   dwFlags ) { return E_FAIL; }
  2245.     STDMETHOD( GetPrivateData )( REFGUID riid, VOID* pvData, DWORD  *pcbData ) { return E_FAIL; }
  2246.     STDMETHOD( FreePrivateData )( REFGUID riid ) { return E_FAIL; }
  2247.     STDMETHOD( SetPriority )( DWORD PriorityNew ) { return E_FAIL; }
  2248.     STDMETHOD_( DWORD, GetPriority )() { return 0; }
  2249.     STDMETHOD( PreLoad )() { return E_FAIL; }
  2250.     STDMETHOD( GetType )() { return E_FAIL; }
  2251.     
  2252.     // IDirect3DSurface9 methods
  2253.     STDMETHOD( GetContainer )( REFIID riid, void **ppContainer ) { return E_FAIL; }
  2254.     STDMETHOD_( D3DSURFACE_DESC, GetDesc )() { return m_Desc; }     
  2255.     STDMETHOD( LockRect )( D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD dwFlags )
  2256.     {
  2257.         // Assume the entire surface is being locked.
  2258.         pLockedRect->Pitch = m_Desc.Width * 4;
  2259.         pLockedRect->pBits = m_pData;
  2260.         return S_OK;
  2261.     }
  2262.     STDMETHOD( UnlockRect )() { return S_OK; }
  2263.     STDMETHOD( GetDC )() { return E_FAIL; }
  2264.     STDMETHOD( ReleaseDC )() { return E_FAIL; }
  2265.     BOOL Create( int iWidth, int iHeight )
  2266.     {
  2267.         m_pData = new BYTE[iWidth * iHeight * 4];
  2268.         if( !m_pData ) return FALSE;
  2269.         m_Desc.Format = D3DFMT_A8R8G8B8;
  2270.         m_Desc.Type = D3DRTYPE_SURFACE;
  2271.         m_Desc.Usage = 0;
  2272.         m_Desc.Pool = D3DPOOL_SYSTEMMEM;
  2273.         m_Desc.MultiSampleType = D3DMULTISAMPLE_NONE;
  2274.         m_Desc.MultiSampleQuality = 0;
  2275.         m_Desc.Width = iWidth;
  2276.         m_Desc.Height = iHeight;
  2277.         return TRUE;
  2278.     }
  2279. };
  2280. //-----------------------------------------------------------------------------
  2281. // Name: GetCloneSurface
  2282. // Desc: Fake a Direct3D surface so we don't have to actually create a direct3d
  2283. //       device object.
  2284. //-----------------------------------------------------------------------------
  2285. IDirect3DSurface9* GetCloneSurface( int iWidth, int iHeight )
  2286. {
  2287.     DidcvDirect3DSurface9Clone *pSurf = new DidcvDirect3DSurface9Clone;
  2288.     if( !pSurf ) return NULL;
  2289.     if( !pSurf->Create( iWidth, iHeight ) )
  2290.     {
  2291.         delete pSurf;
  2292.         return NULL;
  2293.     }
  2294.     return( IDirect3DSurface9* )pSurf;
  2295. }