Author's Guide to Transforms

This article should be considered required reading for transform authors. It contains important information that you need in order for your transform to function correctly under Microsoft DirectX Transform containers. It also includes conventions and tips that are useful for writing transforms.

This article contains the following sections.

  • Things You Need to Know About All Transforms
    • Name
    • ProgIDs
    • Component Categories
    • Property Pages
    • IDXEffect Interface
    • Quality Property on IDXTransform
    • MapBoundsIn2Out
    • Generation IDs
    • Author-time vs. Run-time Transforms
    • DirectX Transform Security Mechanism
    • Checking for Optional Inputs
    • Supporting Relative URLs
    • Quick Setup
    • Transforms and Clip Lists
  • Things You Need to Know About Image Transforms
    • Surface Pick Correlation
    • Supporting IDXTScaleOutput

Things You Need to Know About All Transforms

This section contains the following topics.

  • Name
  • ProgIDs
  • Component Categories
  • Property Pages
  • IDXEffect Interface
  • Quality Property on IDXTransform
  • MapBoundsIn2Out
  • Generation IDs
  • Author-time vs. Run-time Transforms
  • DirectX Transform Security Mechanism
  • Checking for Optional Inputs
  • Supporting Relative URLs
  • Quick Setup
  • Transforms and Clip Lists

Name

Use a user-friendly name for the component. This is the name that visual authoring tools exposes to users. Names such as Slide, Explode, and Fade describe what the transform does and are easy to remember.

ProgIDs

A ProgID is used for hosting transforms. A ProgID is constructed in the following format.

  • Specify the prefix of component categories: DXImageTransform or DXSurface. In the case of transforms, this indicates the type of output.
  • Specify the source company.
  • Specify the name of the effect.
  • Specify the version.

For example: FilterType.CompanyName.FilterName.1

You also need to make provisions for the version number to be optional when a ProgID is used. In that case, if the version is not specified in the ProgID, the latest available one is used. It is important that you use the following .rgs file as a template in order for the version-independent ProgID to work correctly.

    HKCR
{
    FilterType.CompanyName.FilterName.1 = s 'FilterName'
    {
        CLSID = s '{2EF67ED9-1CE2-4453-94F2-125967DB8E09}'
    }
    FilterType.CompanyName.FilterName = s 'FilterName'
    {
        CurVer = s 'FilterType.CompanyName.FilterName.1'
        CLSID = s '{2EF67ED9-1CE2-4453-94F2-125967DB8E09}'
    }
    NoRemove CLSID
    {
        ForceRemove {2EF67ED9-1CE2-4453-94F2-125967DB8E09} =
        s 'FilterName'
        {
            ProgID = s 'FilterType.CompanyName.FilterName.1'
            VersionIndependentProgID =
            s 'FilterType.CompanyName.FilterName'
            ForceRemove 'Programmable'
            InprocServer32 = s '%MODULE%'
            {
                 val ThreadingModel = s 'Both'
            }
        }
    }
}

Component Categories

Transforms can declare themselves in the following categories.

  • CATID_DXImageTransform
  • CATID_DXSurface
  • CATID_DXAuthoringTransform

The second and third categories are unrelated to the first. Transforms are always in the first category, and optionally in the second or third or both. The second category is useful for procedural surfaces, which also function as image transforms. Procedural surfaces are useful when categorized distinctly because they can be used as surface inputs to transforms. For details on the third category, see the following section on the distinction between authoring and run-time transforms.

For corresponding GUID values, refer to Dxtguid.c in the Include directory.

Property Pages

If you want your transform to have user-set variables that define properties specific to your transform, you must define a custom interface. If you define a custom interface, inherit from IDispatch if your transform is not time-based; or inherit from IDXEffect if your transform performs modifications over time.

After you write a custom interface, you need to design a property page, which is a user interface used to change the properties specific to your transform. For example, if your transform is like the GradientWipe sample transform, you might want to define a custom interface that sets the width of the alpha blend used with the transform. In this case, you should also create a property page with a dialog box used to enter the percentage of the output width to use for the alpha blend.

IDXEffect Interface

The following code example shows how the IDXEffect interface is declared in the Dxtrans.idl file.

interface IDXEffect : IDispatch
{
    HRESULT get_Capabilities([out, retval] long *pVal);
    HRESULT get_Progress([out, retval] float *pVal);
    HRESULT put_Progress([in] float newVal);
    HRESULT get_StepResolution([out, retval] float *pVal);
    HRESULT get_Duration([out, retval] float *pVal);
    HRESULT put_Duration([in] float newVal);
};

This is an optional interface for your transform. It exposes a standard way for controlling animated effects. This makes it convenient for external applications to control animations through scripting. The transform might have other animation parameters that would be exposed through custom interfaces.

Capabilities are a way for the transform to indicate whether the animation is periodic. If so, the transform output at IDXEffect::Progress = 0 is the same as the output at IDXEffect::Progress = 1. It is also a way to indicate that the transform is a morph. A morph transform takes two inputs; the result is the first input at IDXEffect::Progress = 0 and the second input at IDXEffect::Progress = 1.

The IDXEffect::Progress is the primary animation property, which has a range from 0.0 to 1.0. To produce an animated effect, you step sequentially through values of IDXEffect::Progress, displaying transform output at each step. Typically, at each step you set IDXEffect::Progress to the desired value and then call the IDXTransform::Execute method to obtain the corresponding result.

The StepResolution property is optional but is useful if the primary animation property leads to different results at discrete points in the [0,1] range. For example, if StepResolution is 0.25, the transform only produces five distinct results. If StepResolution is set to zero, the transform produces a continuous range of results.

CDXBnds::Duration is a recommended amount of time for the primary animation property to change from zero to one. It is optional for a container to use this recommended duration. There is also a IDXEffect::get_Duration method for use by visual authoring tools. This enables the transform to persist a modified duration value from the originally specified one.

Quality Property on IDXTransform

The Quality property for a transform exists so that the performance of a transform that requires many calculations can be adjusted. The Quality property is represented by a float that should be set between 0.0 and 1.0, with 1.0 indicating the highest quality. The value can be set and retrieved using the IDXTransform::SetQuality and IDXTransform::GetQuality methods.

MapBoundsIn2Out

This method takes an array of DXBNDS objects (inBnds) whose number is equal to the number of inputs the transform accepts on IDXTransform::Setup, and in the corresponding order. The transform fills an output DXBNDS structure (outBnds), which represents the bounds of the output of the transform, resulting from a call to IDXTransform::Execute. These bounds are based on the current property settings where the inputs passed to IDXTransform::Setup have the bounds specified in inBnds. If NULL is passed for the input bounds parameter, this method uses the bounds of the current input data objects.

This method is important because containers of transforms typically need to obtain bounds of the transform outputs based on the extents of their inputs before the specific inputs are available. Extent analysis is done before actual inputs are available for a variety of optimizations, including culling, pruning, and dirty rectangle techniques. It is also used for setting the z-resolution in the rendering device, which tends to be a critical resource that should be used carefully.

Therefore, transforms need to have a way to report their output extents based on the settings of their parameters and the bounds of their inputs. These bounds do not need to be tight because it is acceptable if they describe a bounding box that is larger than the actual box. They do, however, need to be true bounds that are guaranteed not to be exceeded by the object. If they are not true bounds, a variety of rendering artifacts might result. These can include dirty pixels because of dirty rectangle optimization and front/back clipping because of underestimates of the z-range.

To simplify Microsoft DirectX Transform, an implementation of MapBoundsOut2In is provided in the base class. It calls a DetermineBnds method on the transform and passes a bound as an in/out parameter. The result is the union of the provided input bounds.

The transform then needs to manipulate the provided bound any way it desires into a bound for the output. There are two helper functions of the CDXBnds class that operate on bounds and that are provided for transforms. One is Expand and the other is Scale. The former is additive, the latter is multiplicative, and both are in relation to the center of the bound. That is, Expand adds half the given x-, y-, and z-values to the maximum component and subtract this value from the minimum component of the corresponding bounds. Scale multiplies the given x-, y-, and z-components of the bounds relative to the midpoint of these components. In most cases an application of one or both operations with proper parameters will suffice for determining the output bound. The bound parameter to DetermineBnds that carried the input bound is intended to be overwritten with the resulting output bound.

One simplifying approach that a transform might use is to determine a lifetime maximum x-, y-, and z-scale across the whole progression of an effect and repeatedly scale the input bound by these same scale factors. This lends itself to simplicity, but in certain cases, it results in overly exaggerated bounds that render the container's optimizations ineffective.

Alternatively, a sophisticated transform can implement its own MapBoundsOut2In to estimate the output bound based on the presently set parameters and input extents. Remember that the tighter the reported bound is, the more effective the container optimizations will be.

The DXETool application provides a way to test the correctness of the reported bounds at the time of an execute by checking them against the actual bounds as obtained from the resulting output.

Generation IDs

The generation ID associated with any surface represents a version number of the object. To make sure your transform is using the most recent version of the inputs, you should check their generation IDs at the beginning of your execution function. When you modify your transform's output, you should increment the output's generation ID. See the CDXBaseNTo1 helper functions for methods to manage generation IDs for your transform.

On a call to IDXTransform::Execute, a transform needs to check these generation IDs and determine if one of its inputs has changed since the last execution. If the transform caches its inputs, which transforms often do as an optimization, and the input's generation ID has changed, the transform needs to regenerate its input cache.

Furthermore, a transform needs to increment the generation ID of its output every time the output changes. A container needs to check the generation ID of the output to control any optimization scheme it might have. Generation IDs are also useful for cases of asynchronous loads, where a container makes the request for a new resolution and learns that the resulting new output is ready based on checking its generation ID. This can be the case for images from the Internet.

Author-time vs. Run-time Transforms

Certain parties might be interested in this distinction because they want to sell the author-time version of their transforms, but freely distribute their run-time version. The typical distinction between the two is as follows.

  • The author-time version is copyrighted, so that when a user hosts it in a visual authoring tool, the user knows that it is copyrighted. See the following section and the Wipe example for details on how this is done.
  • The author-time version typically includes property pages for ease of use in a visual authoring tool; the run-time version will not include property pages. This also helps keep the size of the run-time version smaller.
  • The author-time version is capable of saving persistent properties, including its custom properties (important for visual authoring tools). The run-time version cannot save its properties.

The Wipe sample shows how to produce both an author-time and a run-time version of a transform from a common source. It also illustrates how to tag an author-time transform with a copyright string. The example is located in the Samples\Multimedia\Dtrans\C++\Wipe\Wipedll folder.

DirectX Transform Security Mechanism

Microsoft DirectX Transform allows for a security mechanism for any transforms you write. For more information, see the Copyright Information article.

Checking for Optional Inputs

For optional inputs, it is acceptable for a container to either not provide the input parameter completely, or to provide NULL in place of the input in the call to IDXTransform::Setup. This implies that the transform needs to check if an optional input is NULL and deal with it appropriately. If a transform uses the helper function GetInput, both cases are accounted for automatically, and the helper function reports that no input is present in both cases.

Supporting Relative URLs

Because transforms can be so easily integrated into Web pages, you might want your transform to support relative URLs if a URL is one of its properties. To do this, a transform must support the IBindHost interface. It is an optional interface for transforms to support only if they are interested in obtaining a site-specific bind host. A container is responsible to query for this interface, and if available, to call IDXTBindHost::SetBindHost with the proper parameter. For more information about this interface, see the Platform software development kit (SDK).

For example, transforms that require a URL as a property can use the container's bind host to obtain a base URL and to take a relative URL as the custom property. This helps in content writing so that absolute URLs are not required.

Quick Setup

There is no explicit support for this in the Microsoft DirectX Transform architecture. Every transform is expected to use specific knowledge of its functionality to perform a setup efficiently.

Transforms and Clip Lists

A Microsoft DirectDraw surface can use a clipping rectangle to define a valid region for drawing onto the output surface. If there are multiple valid drawing regions on the output surface, DirectDraw uses a clip list to define them. Regions of the surface that are not covered by the clip list remain unchanged by all drawing operations.

When writing transform output to DirectDraw surfaces such as video memory, the Microsoft DirectX Transform base class does not use clip list information of the destination surface to render its output. To render the output into the correct regions, it is first necessary to call the IDXTransform::Execute on each region of the clip list and assemble the clipped output object in system memory. When this is complete, the DirectDraw surface can be written directly to video memory.

Things You Need to Know About Image Transforms

This section contains the following topics.

  • Surface Pick Correlation
  • Supporting IDXTScaleOutput

Surface Pick Correlation

The following interface is intended for pick correlation.

interface IDXSurfacePick : IUnknown
{
    HRESULT PointPick([in]const DXVEC *pInPoint,
        [out]long * plInputSurfaceIndex,
        [out]DXVEC * pOutPoint );
}

It provides for pick correlation between a given point and the inputs to an image transform. It returns the hit input and the point at which the hit happened in the local coordinates of that input.

This interface is essential for image transforms because they might composite their results to a surface provided by the container at which point the information required for the pick correlation to the input is usually lost. The correlation needs to occur before the final compositing takes place.

There are helper functions that aid in this pick correlation. See the Wipe example to see how they are used. This example is located in the Samples\Multimedia\Dtrans\C++\Wipe\Wipedll folder. Also, for single image input and single image output transforms, the base class does the pick correlation with no need for added code in the transform itself. The base class makes use of the MapBoundsOut2In of the transform, which must be implemented correctly for the base class to work correctly.

It is possible that an image transform either has no inputs or cannot map the pick event back to one of the inputs. In those cases, IDXSurfacePick::PointPick returns DXT_S_HITOUTPUT if the pick point intersects the output surface. For example, this is the case for the gradient procedural surface.

Supporting IDXTScaleOutput

This is an optional interface for zero input image transforms or DXSurfaces to consider supporting. Containers invoke the IDXTScaleOutput::SetOutputSize method on this interface to inform the transform of the desired size (width and height, in pixels) to render to. For example, this interface enables a useful exchange of information between a container and a procedural surface. The DXETool application queries for this interface and invokes it upon window resize.