Implementation Guide

This overview provides a procedure for building an image transform DLL using Microsoft Visual C++ and the Microsoft DirectX Transform interfaces.

  1. Create a new Active Template Library (ATL) project from Visual C++ by opening the File menu and choosing New command. Select the Projects tab, and then ATL COM AppWizard.

  2. Enter the new project name and choose OK.

  3. Set the server type by selecting the Dynamic Link Library option button and choosing Finish.

  4. Open Stdafx.h and insert the following code immediately after the include statement for Atlcom.h.

    #include <Dxtrans.h>
    #include <Dtbase.h>
    #include <Dxatlpb.h>
    
  5. Open Stdafx.cpp and insert the following include statements as the last lines of the file immediately after the include of Atlimpl.cpp.

    #include <Dtbase.cpp>
    #include <Dxtguid.c>
    #include <Atlctl.cpp>
    
  6. Open the project .idl file and add the following line after the import of Ocidl.idl.

    import "Dxtrans.idl";
    
  7. Build the project. If the compiler cannot find one or more of the files, you have not yet added the Microsoft DirectX Media directory to your path. You can do one of the following:

    • To change your global settings to always use the DirectX Media directory, open the Tools menu and choose the Options command. Choose the Directories tab, and add the Workshop\IE6_lib\Include and Workshop\IE6_lib\Lib directories to the corresponding categories, respectively. Make sure each is the first entry in its list.
    • To set the DirectX Media directory for only this project, select Project, then Settings from the main menu bar. In the Settings For drop-down list box, select All Configurations. Select the C/C++ tab, select Category of Preprocessor, add the directory to the Additional Include Directories control, and choose OK.
  8. Add a new ATL object to the project after you confirm that the project builds and links.

    • On the Insert menu, choose New ATL Object.
    • In the dialog box, select Simple Object and choose Next.
    • From the Names tab, enter a name for the object in the Short Name text field (named "MyEffect" in the following example).
    • Choose the Attributes tab.
    • Under Threading Model, choose Both.
    • Under Interface, choose Dual.
    • Under Aggregation, choose Yes.
    • Select the Free Threaded Marshaler check box.
    • Choose OK.
  9. Find the definition of your class's interface in the .idl file and change the inheritance from IDispatch to IDXEffect.

  10. If your transform will use custom properties, you must add a property page to the project.

    • Choose Insert, and then New ATL Object.
    • On the dialog box that appears, choose Controls in the left window, choose Property Page in the right window, and choose Next.
    • On the Names tab, enter a name for the object in the Short Name text field (named "MyEffectPP" in the following example).
    • Choose the Attributes tab.
    • Under Threading Model, choose Both.
    • Under Interface, choose Dual.
    • Under Aggregation, choose Yes.
    • Select the Free Threaded Marshaler check box.
    • Choose OK.
  11. Open the MyEffect.h file and make the following changes.

    • Make your class inherit from the base class by replacing the following code:

      public CComObjectRootEx<CComMultiThreadModel>,
      

      with the line:

      public CDXBaseNTo1,
      
    • Inherit from the following additional items.

      public CComPropertySupport<CMyEffect>,
      public IObjectSafetyImpl2<CMyEffect>,
      public IPersistStorageImpl<CMyEffect>,
      public ISpecifyPropertyPagesImpl<CMyEffect>,
      public IPersistPropertyBagImpl<CMyEffect>
      
    • Add the following lines of code immediately before END_COM_MAP in the COM_MAP.

      COM_INTERFACE_ENTRY(IDXEffect)
      COM_INTERFACE_ENTRY_IID(IID_IObjectSafety,
      IObjectSafetyImpl2<CMyEffect>)
      COM_INTERFACE_ENTRY_IMPL(IPersistStorage)
      COM_INTERFACE_ENTRY_IMPL(ISpecifyPropertyPages)
      COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag)
      COM_INTERFACE_ENTRY_CHAIN(CDXBaseNTo1)
      
    • Change the registration entry from:

      DECLARE_REGISTRY_RESOURCEID(IDR_MyEffect)
      

      to:

      DECLARE_REGISTER_DX_TRANSFORM(IDR_MyEffect,
                      CATID_DXImageTransform)
      
    • Declare the object as aggregatable by adding the following line.

      DECLARE_POLY_AGGREGATABLE(CMyEffect)
      
    • Add the following in the class declaration.

      DECLARE_IDXEFFECT_METHODS(Caps)
      

      Caps specifies the value you want your transform to return from the get_Capabilities method. This can be a combination of the flags in the DXEFFECTTYPE enumeration, or zero. In the case of the sample, it is not a morph (DXTET_MORPH, two input transforms), and it is not periodic (DXTET_PERIODIC, where the result at one is not the same as the result at zero), so the Caps value is zero.

    • Add a property map for the object by inserting the following within the class definition body.

      
      BEGIN_PROPERTY_MAP( CMyEffect )
          PROP_ENTRY("Description", DISPID_MyProperty,
          CLSID_MyEffectPP )
          PROP_PAGE( CLSID_MyEffectPP )
      END_PROPERTY_MAP()
      

      There must be a PROP_ENTRY for each custom property you want your transform to support. You can fill these items in later, when you define these properties for your transform. The PropPageClsid is located at the top of your MyEffectPP.h header file. If your transform has no custom properties, the property map should be omitted.

  12. Add the core of the program. There are three main steps to add: Creation, Setup, and Execution.

  13. Add support code for the transform property page.

    • For each private data member of your transform that represents a custom property, you need to implement put_ and get_ access functions. These are often declared in your MyEffect.h header file in the following manner.

      STDMETHOD( get_MyProperty )( float *pVal );
      STDMETHOD( put_MyProperty )( float newVal );
      
    • Add dispatch interface support for the MyEffect interface. Create an enumeration for the dispatcher identifiers, with each property an element of the enumeration. Place it just before the entry for MyEffect in the project's .idl file.

      typedef enum MyEffectDISPID
      {
          DISPID_MyEffect_MyProperty1 = DISPID_DXE_NEXT_ID,
          DISPID_MyEffect_MyProperty2,
      } MyEffectDISPID;
      

      Add dispatcher methods to the MyEffect interface, as shown in the following code example.

      
      interface MyEffect : IDXEffect
      {
          [propget, id(DISPID_MyEffect_MyProperty1), helpstring(
          "property MyProperty1")] HRESULT MyProperty1
          ([out, retval] float *pVal);
          [propput, id(DISPID_MyEffect_MyProperty1), helpstring(
          "property MyProperty1")] HRESULT MyProperty1
          ([in] float newVal);
          [propget, id(DISPID_MyEffect_MyProperty2), helpstring(
          "property MyProperty2")] HRESULT MyProperty2
          ([out, retval] float *pVal);
          [propput, id(DISPID_MyEffect_MyProperty2), helpstring(
          "property MyProperty2")] HRESULT MyProperty2
          ([in] float newVal);
      };
      

      You should then fill out the PROPERTY_MAP in the MyEffect.h file.

    • Inside the resource file for the project, there should be a dialog box entry for your property page. You must edit this to reflect the custom properties that people can change on your transform.

    • In the MyEffectPP.cpp file, override the OnInitDialog method. It should use the get_MyProperty method for each property to set the initial values of the dialog box.

    • In the MyEffectPP.cpp file, override the apply method. It should read each value from the dialog box and use the put_MyProperty method to set the values in the transform.

  14. Implement surface picking. For image transforms with more than one input, you need to override the CDXBaseNTo1::OnGetSurfacePickOrder function with a function that chooses an input surface based on a selected point on the output surface. For a custom surface picking implementation, you must override the CDXBaseNTo1::OnSurfacePick method.