Retired Document
Important: Apple recommends that developers explore QTKit and Core Video for new development in this technology area. See QTKit Framework Reference and Core Video Programming Guide for more information.
Creating New Video Effects
This chapter discusses how to write your own video effects. If you are only interested in building applications that use effects, you can skip this chapter.
QuickTime video effects are implemented as Component Manager components, the standard mechanism for extending QuickTime. To implement your own effect, you create a new effect component. An effect component is a specialized type of image decompressor component.
This chapter walks you through the implementation of a sample effect component. The sample effect is built on a framework of code that you can reuse when you implement your own effect component.
What Effects Components Do
The basic task of every effect component is very simple. The component is passed zero or more source frames and must produce a single destination frame. The destination frame is the source frame or frames after processing by the effect-rendering algorithm.
The component must provide a set of services that QuickTime can call. These services allow QuickTime (or any other client software that uses your component) to perform actions such as these:
Open a connection to your component.
Retrieve information about your effect, particularly descriptions of the parameters your effect can take.
Set the source or sources for the effect.
Set the destination for the effect.
Request that a single frame of the effect be rendered.
Cancel the rendering of a frame.
Close the connection to your component.
Your effect component must be able to service such requests. To do so, it implements a set of standard interface functions that are called through a component dispatch function. Details of these functions are given in the section The Effect Component Interface.
The main task of the effect component is to implement the specific algorithm that transforms source frames into a destination frame. You need to supply versions of your algorithm for each bit depth and pixel format that your component supports. Choosing which bit depths and pixel formats to support, and implementing algorithms for each combination of these, are a significant part of building your effect component.
In addition, your effect component must provide a parameter description that describes the parameters that the effect takes. The parameter description can be used by the software that is calling your component to construct a user interface that allows users to change the value of the parameters sent to your component. This is described in detail in the section Supplying Parameter Description Information.
The Effect Component Interface
Effect components, as with all other types of QuickTime components, must implement a defined set of functions. To ease the component development process, the Generic Effect component is provided for you. This component implements many of the “housekeeping” functions that all components must perform. In most cases, these default implementations are appropriate for your effect, and you simply delegate these functions to the generic effect component. In the rare instances when you need to provide your own implementations of one of these basic functions, you can override the generic version and provide your own implementation.
By delegating many of the functions to the generic effect, you not only decrease the number of functions you must implement, you also produce a smaller effect component, because common code is stored only once, in the generic effect.
The framework code provided in the dimmer effect sample (The Dimmer Effect), shows how to delegate interface functions to the generic effect component.
Your component must provide implementations for these functions:
Term |
Definition |
---|---|
|
Opens a connection between the client software and your component. |
|
Closes the connection between the client and your component. |
|
Returns the version number of your component. |
|
Called once before a sequence of frames are rendered. This gives your effect the chance to set up variables that will alter their value during the execution of a sequence of frames. |
|
Called once before a frame is rendered. Your component can safely perform operations that move memory when this function is called. |
|
Called to render a frame. Because this function can be called asynchronously, it is not safe to perform operations that may move memory during this call. |
|
Cancels the rendering of a frame. If your component supports asynchronous operation, this function can be called while a frame is being rendered. |
|
Returns a parameter description atom container, as described in the section "Supplying Parameter Description Information" [link s Creating New Video Effects]. |
|
Returns information to the codec manager about the capabilities of your component. |
|
Returns the approximate number of frames per second that your effect is capable of transforming. |
These functions can be categorized into four groups. The Open
and Close
functions deal with maintaining a connection between your component and client software. In most cases, you can implementation these functions using the sample code provided by Apple without modification.
The Version
, GetParameterListHandle
, GetCodecInfo
and EffectGetSpeed
functions return information about your component. The most important of these functions is GetParameterListHandle
, which returns a description of the parameters that your effect can take. See Supplying Parameter Description Information for more details of this what this function should do.
The EffectSetup
function is called immediately before your component is required to render a sequence of frames. On entry, the function contains a description of the sequence that is about to be rendered. Most importantly, it describes the bit depth and pixel format of the sources that your component has to deal with. Your Setup
function can then verify that your component can handle these formats. If it cannot, EffectSetup
should return the “closest” bit depth and pixel format combination that it can handle, and QuickTime will generate versions of the sources and destination in the requested format. This ensures that your effect component is given source and destination buffers in a format that it understands. See The EffectRenderFrame Function for more details.
The most significant function group contains the EffectBegin
, EffectRenderFrame
, and EffectCancel
functions. These functions contain the implementation of your effect algorithm. In most cases, you can implement the EffectCancel
function simply by using the sample code provided by Apple. The implementation of the EffectBegin
and RenderFrame
functions is covered in Implementing the EffectBegin and EffectRenderFrame Functions.
Full details of the interface functions your component must supply are given in Component-Defined Functions.
Supplying Parameter Description Information
Your effect component must supply information that describes the parameters that your effect takes. This information is used to create an appropriate user interface for setting the parameters to your effect. The parameter description lists your effect’s parameters and their data types and indicates the appropriate selection interface for each parameter, such as a slider or a pull-down list, as well as information such as the minimum, maximum, and default values for each parameter. Each parameter is described using a specific format, which is shown in The Parameter Description Format.
Your effect component returns its parameter description information through the GetParameterListHandle
function. The easiest way to provide this information back to the client software is to add an 'atms'
resource to your component. The 'atms'
resource contains the parameter descriptions in the required format. You can then retrieve the resource by calling the GetComponentResource
function, returning it to the client through your implementation of GetParameterListHandle
, as shown in Listing 4-1.
Listing 4-1 Implementing the GetParameterListHandle function using GetComponentResource
pascal ComponentResult GetParameterListHandle(EffectGlobals *glob, |
Handle *theHandle) |
{ |
OSErr err = noErr; |
err = GetComponentResource((Component) glob->self, |
OSTypeConst('atms'), |
kEffectatmsRes, |
theHandle); |
return err; |
} |
By implementing the GetParameterListHandle
function in this way, you can simplify the process of packaging the necessary information in the proper format.
Implementing the EffectBegin and EffectRenderFrame Functions
The core of implementing an effect component is implementing the EffectBegin
and EffectRenderFrame
functions. Together, these functions handle the rendering of a single frame of the effect.
The EffectBegin
function is called immediately before each frame is to be rendered. It is guaranteed that this function is never called from an interrupt, so it is safe to perform actions that could move memory within this function. In general, the EffectBegin
function should set up the internal state of your component so it has all the information it needs to render a single frame.
The EffectRenderFrame
function is called to actually render the frame. This can be called at interrupt time, so it is not safe to move or allocate memory in this function. You should also take care not to call functions that would do so. Your EffectRenderFrame
function should actually render a single frame of your effect.
The EffectBegin function
The main tasks that the EffectBegin
function should perform are:
Ensure that the effect component has valid references to the current sources. If the component does not have a reference to the sources, or the sources have changed since the last call to
EffectBegin
, they must be updated.Ensure that the component has a valid reference to the current destination. If the component does not have a reference to the destination, or the destination has changed since the last call to
EffectBegin
, it must be updated.Ensure that the component has the current parameter values. If the source or destination has changed, or the component does not currently have values for the effect parameters, these parameter values are read.
If any of the parameter values are tweened, tweening is performed to determine the actual value for those parameters.
Checking Source and Destination References
The following code checks to see if the destination has changed since the last call to the EffectBegin
function:
if (p->conditionFlags & (codecConditionNewClut+ |
codecConditionFirstFrame+codecConditionNewDepth+ |
codecConditionNewDestination+codecConditionNewTransform)) |
If this evaluates to true
, the destination has changed. This expression checks a series of flags that are passed to the EffectBegin
function in the conditionsFlags
field of the decompressParams
parameter. When the destination is changed, QuickTime sets these flags to alert the effect component to update its internal state.
The most important information that you need to store about the new destination is its base address and its rowBytes
value. These values allow you to draw onto the destination surface.
Listing 4-2 shows an example function that stores information in the effect component’s global data structure about the destination PixMap
passed to the function.
Listing 4-2 Storing information about a new destination frame
static long BlitterSetDest(BlitGlobals*glob, // input: our globals |
PixMap *dstPixMap, // input: pixels we will draw into |
Rect *dstRect) // input: area of pixels we will draw into |
{ |
OSErr result = noErr; |
long offsetH,offsetV; |
char *baseAddr; |
// Calculate the based address according to the format of the |
// destination PixMap |
offsetH = (dstRect->left - dstPixMap->bounds.left); |
if (dstPixMap->pixelSize == 16) |
{ |
offsetH <<= 1; // 1 pixel = 2 bytes |
} |
else |
{ |
if (dstPixMap->pixelSize == 32) |
{ |
offsetH <<= 2; // 1 pixel = 4 bytes |
} |
else |
{ |
result = -1; // this is a data format we can't handle |
} |
} |
offsetV = (dstRect->top - dstPixMap->bounds.top) |
* dstPixMap->rowBytes; |
baseAddr = dstPixMap->baseAddr + offsetH + offsetV; |
glob->dstBaseAddr = baseAddr; |
glob->dstRowBytes = dstPixMap->rowBytes; |
return result; |
} // BlitterSetDest |
The process for checking for new sources is broadly similar. The CodecDecompressParams
data structure passed into the EffectBegin
function has a field called majorSourceChangeSeed
. This contains a seed number generated from the characteristics of the set of sources for the effect. If the sources change, the majorSourceChangeSeed
value will also change, so the effect can store the current value in its global data structure and compare it to the current value. If they are different, the effect knows its sources have changed.
When the effect detects that one or more of its sources have changed, it must iterate through all its sources and reload information about them.
Listing 4-3 shows example code that performs these operations. Listing 4-4 shows the BlitterSetSource
function that is called by this example code. The BlitterSetSource
function is analogous to the BlitterSetDest
function shown in Listing 4-2.
Listing 4-3 Checking for source changes
// Check to see if one or more sources have changed |
if (p->majorSourceChangeSeed != glob->majorSourceChangeSeed) |
{ |
// grab start of input chain for this effect |
source = effect->source; |
// we can play with up to kMaxSources sources, so go get them |
while (source != nil && numSources < kMaxSources) |
{ |
// now give that source to our blitter |
err = BlitterSetSource(glob, numSources, source); |
if (err != noErr) |
goto bail; |
source = source->next; |
++numSources; |
} |
} |
Listing 4-4 Storing information about a new source frame
static long BlitterSetSource(BlitGlobals*glob, // input: our globals |
long sourceNumber, // input: source index to set |
CDSequenceDataSourcePtr source) // input: source value |
{ |
OSErr err = noErr; |
if (sourceNumber >= kMaxSources) |
{ |
// too many sources for us to handle |
return noErr; |
} |
else |
{ |
// a source we can handle, save it away |
err = RequestImageFormat(source, glob->width, glob->height, |
glob->dstPixelFormat); |
if (err == noErr) |
{ |
glob->sources[sourceNumber].src = source; |
} |
else |
{ |
glob->sources[sourceNumber].src = nil; |
} |
} |
return (err); |
} // BlitterSetSource |
Reading Parameter Values
Listing 4-5 shows how to read the value of a non-tweened parameter. The QTFindChildByID
function is used to retrieve the atom containing the parameter value. The parameter value is then copied from the atom using the function QTCopyAtomDataToPtr
. If the value is successfully copied, it is endian-flipped to ensure it is in native-endian format (parameter values are always stored in big-endian format). If the copy failed, a default value is provided.
The value retrieved from the parameter is stored in the component’s global data structure (called, in this example, global
-> blitter
). This allows the value to be used by other functions, notably the component’s EffectRenderFrame
function.
Listing 4-5 Reading a parameter value
{ |
Ptr data = p->data; |
QTAtom atom; |
QTAtomID atomID = 1; |
long actSize; |
// Find the 'sden' atom |
atom = QTFindChildByID((QTAtomContainer) &data, |
kParentAtomIsContainer, |
OSTypeConst('sden'), // The name of the parameter |
atomID, // The ID of the parameter |
nil); |
// Copy the parameter value from the atom |
if (QTCopyAtomDataToPtr((QTAtomContainer) &data, |
atom, |
false, |
sizeof(long), |
&((glob->blitter).scratchDensity), |
&actSize)!=noErr) |
{ |
// If the copy failed, use a default value for this parameter |
((glob->blitter).scratchDensity) = 1; |
} |
else |
{ |
// Otherwise, the copy succeeded, so endian flip and store the |
// parameter value |
((glob->blitter).scratchDensity) = EndianS32_BtoN(((glob->blitter).scratchDensity)); |
} |
} |
If the parameter value can contain a tweened value, you can use code similar to that shown in Listing 4-6 to retrieve the parameter value. The functions InitializeTweenGlobals
and CreateTweenRecord
are utility functions that Apple provides as part of the dimmer effect sample framework (see The Sample Effect Component).
Listing 4-6 Reading a tweened parameter value
{ |
Ptr data = p->data; |
OSErr err; |
long index = 1; |
err = InitializeTweenGlobals(&glob->tweenGlobals, p); |
if (err!=noErr) |
goto bail; |
// Make our tweener, return if we already have it |
err = CreateTweenRecord(&glob->tweenGlobals, |
&glob->percentage, |
OSTypeConst('pcnt'), // The name of the parameter |
1, // The ID of the parameter |
sizeof(Fixed), |
kTweenTypeFixed, |
(void*) 0, |
(void*) fixed1, |
effect->frameTime.virtualDuration); |
if (err!=noErr) |
goto bail; |
glob->initialized = true; |
} |
Tweening Parameter Values
If you have specified that one or more of your parameter’s values can be tweened, you need to implement code to perform the tweening in the EffectBegin
function.
Listing 4-7 shows an example of tweening a parameter value. The current frame time is retrieved and subtracted from the effect’s virtualStartTime
. This calculates how far through the execution of the current effect sequence we are, expressed as a percentage.
With this information, the code then calls QTDoTween
to interpolate the parameter value, leaving the resulting value in glob
-> comp1Tween
.tweenData
.
Listing 4-7 Tweening parameter values
wide percentage; |
// Find out how far through the effect we are |
percentage = effect->frameTime.value; |
CompSub(&effect->frameTime.virtualStartTime, &percentage); |
// Tween our parameters and get the current value for this frame, prepare |
// to render it when the EffectRenderFrame happens |
{ |
Fixed thePercentage; |
if (glob->percentage.tween) |
QTDoTween(glob->percentage.tween, percentage.lo, |
glob->percentage.tweenData, nil, nil, nil); |
thePercentage = **(Fixed**) (glob->percentage.tweenData); |
// If we are before the half-way point of this transition, we should |
// be fading the first source to black |
if (thePercentage < fixed1/2) |
{ |
(glob->blitter).direction = 1; |
(glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512), thePercentage)); |
} |
// Otherwise, we are fading up onto the new source |
else |
{ |
(glob->blitter).direction = 0; |
(glob->blitter).dimValue = FixedToInt(FixMul(IntToFixed(512), |
thePercentage)) - 255; |
} |
} |
The EffectRenderFrame Function
The EffectRenderFrame
function is called to actually render a single frame of your effect. This is where you transform the sources of your effect into the destination frame, using the algorithm that implements your effect.
This is also where you have to handle multiple bit depth and pixel format combinations.
Internally, QuickTime stores bitmaps in a wide variety of formats. The system can handle images in a number of bit depths and with many different pixel formats. Effect components must have some ability to handle source and destination frames that are at any of the bit depths and in any of the pixel formats that QuickTime supports.
Obviously, providing a separate implementation of your effect algorithm for every combination of bit depth and pixel format could be an enormous task. Fortunately, QuickTime provides mechanisms for you to limit the number of formats you have to explicitly support.
When your effect component’s EffectSetup
function is called, it is passed information about the bit depth and pixel formats that the source frames are in. Your component should examine the formats and react in one of two ways:
If the format is one of those which your effect does support, the
EffectSetup
function does nothing.If the format is not supported by your effect,
EffectSetup
returns the nearest format that is supported.
In the second case, where you do not directly support the format, QuickTime automatically creates buffers in the format returned by EffectSetup
. The source frames are written into the buffer before EffectRenderFrame
is called, so that source data is always available in a supported format. The destination frame is also buffered, and QuickTime automatically transforms the image into the required format for you.
This way, you only need to support a limited number of image formats, and QuickTime will ensure that EffectRenderFrame
isn’t called with data in any other format.
Handling Multiple Formats
Although you can write separate versions of your effect algorithm for each combination of bit depth and pixel format, Apple recommends that you implement your effect algorithm once for each bit depth. You should then use the Apple-supplied blit macros to automatically generate versions of these implementations for each supported pixel format. This significantly reduces the number of separate implementations you have to maintain, and allows easy support of multiple pixel formats.
The blit macros are contained in the file BltMacros
.h
, which is included with the sample effect framework code.
To use the blit macros in your effect component, you must store each bit depth implementation of your effect algorithm in a separate file. These files are then included into the effect component’s main source code file multiple times, once per pixel format supported. Each inclusion is surrounded by #define
statements that define the pixel format version to be generated.
Each file uses a #include
statement to include
BltMacros
.h
, and all operations that read pixels from a source buffer or write pixels to the destination buffer are performed using appropriate macros.
The macros are automatically converted to the correct operations for the pixel format when the file is included into the main source code. This generates a version of the algorithm for each pixel format.
Finally, code is put into place in the effect component’s EffectRenderFrame
function, which calls the appropriate generated algorithm according to the current bit depth and pixel format of the source buffers.
Implementing a Bit-depth Specific Version of Your Algorithm
Listing 4-8 shows an example implementation of an effect algorithm. The code uses the blit macros to read pixels from the source frame and write them to the destination frame. This example shows a filter that changes a single source. It also shows how to read and alter a single pixel at a time; other effects may handle multiple pixels at a time for efficiency.
The sample shows the following operations for each pixel of the source frame:
Retrieving the next pixel from the source, using the
Get16
(which reads a 16-bit pixel from a memory address) andcnv16SPFto16RG
(which converts a 16-bit pixel in the current pixel format to the standardized 16-bit ARGB format) macros to handle pixel format conversion;
Decomposing the pixel into alpha, red, green and blue components;
Reassembling the alpha, red, green and blue components into a standardized ARGB pixel value;
Writing the pixel value to the destination buffer, using the
cnv16RGto16DPF
(which converts the 16-bit standardized format pixel back into the current buffer’s 16-bit pixel format) andSet16
(which writes a 16-bit pixel to a memory address) macros to handle pixel format conversion.
The actual effect implementation, which would alter the alpha, red, green and blue values of each pixel according to the effect specification, is not shown in this example code.
Listing 4-8 A sample effect algorithm for 16-bit frames
#include <BltMacros.h> |
void EffectFilter16(BlitGlobals *glob); |
void EffectFilter16(BlitGlobals *glob) |
{ |
long height = glob->height; // Local copy of the height of |
// the buffers |
UInt16 *srcA = glob->sources[0].srcBaseAddr; // Local pointer to |
// the first source image |
UInt16 *dst = glob->dstBaseAddr; // Local pointer to the |
// destination |
long srcABump; |
long dstBump; |
// Work out the source and destination "bumps". The rowBytes value |
// gives you the number of bytes in each scanline of an image. This |
// is not necessarily the same as the number of pixels in a scanline |
// multiplied by the number of bytes each pixel occupies. When |
// we copy pixels from source to destination, via our effect |
// algorithm, we need to account for this discrepancy. The following |
// lines lines pre-calculate the differences. |
srcABump = glob->sources[0].srcRowBytes - (glob->width * 2); |
dstBump = glob->dstRowBytes - (glob->width * 2); |
// Now, for every scanline in the source image we are dealing with... |
while (height--) |
{ |
long width = glob->width; |
// ...iterate through every pixel in that scanline |
while (width--) |
{ |
UInt16 thePixelValue; |
// Retrieve the next pixel value |
thePixelValue = Get16(srcA); |
srcA++; |
// Call to blit macros to ensure the pixel format is |
// converted appropriately |
cnv16SPFto16RG(thePixelValue); |
// Get the alpha, red, green and blue values of the pixel |
alpha = 0x8000 & thePixelValue; |
red = (thePixelValue & 0x7C00) >> 10; |
green = (thePixelValue & 0x03E0) >> 5; |
blue = (thePixelValue & 0x001F) >> 0; |
// IMPLEMENT YOUR EFFECT ALGORITHM HERE ON EACH PIXEL |
// Re-assemble the A, R, G and B values into a 16-bit |
// destination pixel |
thePixelValue = alpha | (red << 10) | (green << 5) |
| (blue << 0)); |
// Set the destination pixel,first passing it through the |
// appropriate blit macro to |
// ensure the correct pixel format conversion is performed |
cnv16RGto16DPF(thePixelValue); |
Set16(dst, thePixelValue); |
dst++; |
} |
// Bump the source and destination pointers we are using, to |
// avoid problems when moving from one scanline to the next |
srcA = (void *) (((Ptr) srcA) + srcABump); |
dst = (void *) (((Ptr) dst) + dstBump); |
} |
} |
Including the Bit-depth Implementations in Your Effect Code
Once you have produced separate implementations of your effect algorithm for each bit depth you support, you need to include these in your main effect source code file. Each bit depth implementation is included once for every pixel format you support.
Listing 4-9 shows statements to include the 16-bit implementation of the effect into the main effect source code file. The implementation is included three times, for the following pixel formats:
Big-endian 555 RGB
Little-endian 555 RGB
Little-endian 565 RGB
The result of the code in Listing 4-9 is that your effect source code contains three separate versions of the effect algorithm for handling 16-bit sources. These are named EffectFilter16BE555
, EffectFilter16LE555
, and EffectFilter16LE565
, respectively.
Listing 4-9 Including the 16-bit implementation into the main effect source code
// 16-bit, Big Endian 555 pixel format |
#define EffectFilter16 EffectFilter16BE555 |
#define srcIs16BE555 1 |
#define dstIs16BE555 1 |
#include "EffectFilter16.c" |
#undef EffectFilter16 |
#undef srcIs16BE555 |
#undef dstIs16BE555 |
// 16-bit, Little Endian, 555 pixel format |
#define EffectFilter16 EffectFilter16LE555 |
#define srcIs16LE555 1 |
#define dstIs16LE555 1 |
#include "EffectFilter16.c" |
#undef EffectFilter16 |
#undef srcIs16LE555 |
#undef dstIs16LE555 |
// 16-bit, Little Endian, 565 pixel format |
#define EffectFilter16 EffectFilter16LE565 |
#define srcIs16LE565 1 |
#define dstIs16LE565 1 |
#include "EffectFilter16.c" |
#undef EffectFilter16 |
#undef srcIs16LE565 |
#undef dstIs16LE565 |
Calling the Effect Implementations from EffectRenderFrame
Finally, you must provide code inside your EffectRenderFrame
function to call the appropriate implementation of your effect algorithm, depending on the pixel format and bit depth of the source frames you are dealing with. Listing 4-10 shows how to do this for the 16-bit pixel formats.
Listing 4-10 Calling pixel format specific versions of the 16-bit effect implementation
switch (glob->dstPixelFormat) |
{ |
case k16BE555PixelFormat: |
EffectFilter16BE555(glob); |
break; |
case k16LE565PixelFormat: |
EffectFilter16LE565(glob); |
break; |
case k16LE555PixelFormat: |
EffectFilter16LE555(glob); |
break; |
} |
The code to handle the 32-bit pixel formats is an easy extension of the code shown in this section, and can be found in the sample effect component included in the QuickTime SDK and described in detail in the next section.
The Sample Effect Component
This section introduces you to the sample effect component supplied as part of the QuickTime SDK. It takes you through the parts of the code that you will need to change in order to implement your own effect component.
The Dimmer Effect
The sample effect described in this section is a dimmer effect. This simple effect fades the first source to black, then fades up to show the second source. The source code for this effect is provided as a CodeWarrior Pro project as part of the QuickTime SDK.
The Standard Effect Framework
Much of the code required to implement an effect component is the same for all components. Apple has provided a framework of code that you can adapt to create your own effect components. In most cases, you only have to change limited portions of the framework code to create your new component.
Structure of the Framework
The effect framework is one approach to writing effects components. It has been designed to provide most of the basic code required to implement an effect component, leaving the implementation of the effect algorithm to you. It should be possible to write most effects using the framework, though you may need to adapt the framework code for more complex effect components.
The framework implements the required component functions for an effect. The main body of the framework is the Effect
.c file, which contains the source code for the framework and an implementation of the dimmer effect. This file is made up of four main parts:
The global data structures used by the framework. You will need to update some of the data structures to reflect the capabilities of the effect you are implementing.
The dispatcher is the entry point to your component. Because all effects components have the same set of component functions, you should not need to alter the dispatcher.
The internal functions are the set of functions that actually execute your effect. This is where most of your own code will be added.
The component functions are the standard functions called by the dispatcher. These functions call the internal functions to actually execute the effect. For most effects, you won’t need to change much code in this section.
Naming Conventions
All the function and data structure names in the framework are arbitrary. The names have been chosen to reflect their purpose, but you are free to change the names, as long as they remain internally consistent.
If you choose to change the names of the component functions, you will have to change the CALLCOMPONENT_BASENAME
#define
in Effect
.c
. This defines the root of the name used for component functions. For example, if CALLCOMPONENT_BASENAME
is set to SlideEffect
, then the Open component function must be called SlideEffectOpen
, the Close component function must be called SlideEffectClose
, and so forth.
Apple recommends that you do not change the names used in the framework.
Writing an Effect Component Using the Framework
The effect component framework is provided for you to simplify the development of QuickTime video effects components.
The QuickTime SDK includes the folder DimmerEffect
, which contains the framework, complete with associated resources and makefiles. See the ReadMe
file in the DimmerEffect
folder for full installation and use instructions.
To adapt the dimmer framework to create your own component, search through the source code file Effect
.c for the comments CHANGE
. These comments mark the sections of the source code you will need to change to write your own effect.
The following sections take you through the specific changes you need to make to the framework. All the changes except the last, which implements the actual effect algorithm, are made to the Effect
.c file.
Synchronous vs. Asynchronous Processing
The first change you may need to make is to the following #define
:
#define kMaxAsyncFrames 0 |
This value defines the number of frames that can be queued for asynchronous rendering by this effect. If your effect declares that is can handle more than 0
asynchronous frames, frames may be queued for rendering. If you wish to render synchronously, set kMaxAsyncFrames
to 0; otherwise set it to the number of frames that can be held in the queue.
Defining the Number of Sources
Most effects require one or more sources to operate on, though some effects (such as Apple’s fire effect) operate without any sources. The dimmer effect uses two sources: the first is the source to fade to black, the second is the source to fade up on. Most effects transition between two sources. Sometimes, effects control the transition between two scenes by blending in one or more other sources, in which case the effect may require three or more sources. You may also want to implement a filter effect that has only a single source and produces a transformed version of that source.
You set the value of kMaxSources
to the maximum number of sources required by the effect. Effects that can take more than one source should be prepared to handle the case when fewer than the maximum number of sources are actually provided. For example, if your effect expects two sources to transition between and a third source to use as a mask, your code must handle the case where only the two transition sources are provided. In this case you should use a default mask instead of a third source.
Adding to the Global Data Structures
The framework defines two global data structures: BlitGlobals
and EffectGlobals
. The BlitGlobals
structure holds information related to drawing a single frame of the effect, while the EffectGlobals
holds data for the entire effect as it is executed. These data structures are global to an instance of the effect component. That is, if you have multiple instances of the component opened, each instance gets its own copy of both data structures.
You can add fields to the BlitGlobals
data structure to hold information specific to your effect. A set of standard fields are already defined, which hold information used by the framework. You can add your own effect-specific fields between the CHANGE
and END
CHANGE
comments.
The example defines two fields, dimValue
and direction
. These hold the current dim value for the effect and a flag indicating whether it is fading down or up, respectively. Because the dimmer fades the first scene down to black then fades up on the second scene, it also needs to store the dim value between individual frames. This value is stored in the dimValue
field of BlitGlobals
.
You can also add fields to the EffectGlobals
structure. Generally, you will read the values for the parameters to your effect in these fields so that they can be referenced while the effect executes.
Preflighting the Blitter
The internal function BlitterPreflight
is called from EffectSetup
before the first frame of the effect is rendered. This function’s main task is to validate the bit depth that the effect is being requested to support.
The bit depth that the effect is being asked to operate at is passed in the depth
parameter to BlitterPreflight
. The function should return in the same parameter the bit depth at which it wants to operate.
For example, the dimmer effect can operate on 16-bit or 32-bit sources. If either of these values is passed in, it simply returns depth
unaltered. If any other bit depth is requested, it sets depth
to 16, the default bit depth for this effect.
Your effect should validate the bit depth passed in a similar way. Apple recommends that your effect support at least 16- and 32-bit depths.
When you set the depth
parameter to a different value than it was on entry to BlitterPreflight
, QuickTime creates an offscreen buffer for the sources and destination of the effect. All data is passed through these offscreen buffers, to ensure that your effect only sees data in a format it can handle.
Setting the Destination
The BlitterSetDest
function is called from EffectBegin
and is passed the effect’s destination, in the form of a PixMap
. The BlitterSetDest
function should calculate the base address and rowBytes
values for the destination and store these in the BlitGlobals
data structure for future reference.
You need to make changes to this function only if your effect supports destinations in bit depths other than 16-bit and 32-bit.
The BlitterRenderFrame function
This function calls the functions that implement your effect algorithm. The function names to be called are those generated by the blit macros.
The example code supports the three most common pixel formats in 16-bit and 32-bit. If your effect needs to support other bit depths or pixel formats, you need to update the switch
statement in this function so that the appropriate drawing functions are called.
The EffectsFrameClose function
This function is called when the client software has finished using your component. At this time, your component should dispose of any memory it allocated. In particular, you should call DisposeTweenRecord
for each tween record you allocated and then call DisposeTweenGlobals
.
Reading the Effect Parameters
The parameters of the effect are read in the EffectsFrameEffectBegin
function. Your effect should read its parameter values in the section between the CHANGE
and END
CHANGE
comments, reading either non-tweened or (more frequently) tweened values. Example code for both these cases is given in Reading Parameter Values.
Once you have read in the parameter values, you need to tween those parameters that contain tween records. This code should be placed between the second pair of CHANGE
and END
CHANGE
comments. Again, example code to do this is supplied, see Tweening Parameter Values.
Implementing your Effect
The last stage
in adapting the framework is to implement your effect algorithm. You need to provide one implementation per bit depth that your effect explicitly supports, and each implementation must be placed in a separate file. These files are named EffectFilter16
.c, EffectFilter32
.c, and so forth.
The dimmer effect code provides an example of the pixel manipulations that an effect will typically perform, and shows how to use the blit macros to support multiple pixel formats at a given bit depth.
Clearly, the details of these routines are entirely dependent on the effect being implemented.
Adding an 'atms' Resource to your Component
The 'atms'
resource for your effect contains two sets of information. The first set contains the effect information that is used to construct the standard parameters dialog box. This includes items such as the name of your effect and optional copyright information.
The second set contains the parameter information, which is a description of each parameter that your effect takes. If your effect does not take parameters, there is no information in this set.
The structure of an 'atms'
resource is as follows:
resource 'atms' (kEffectatmsRes) { |
7, |
{ |
// The resource body goes here |
}; |
}; |
The header for this resource contains two items: the resource ID, and the number of root level atoms the resource contains.
The first line contains the ID of the 'atms'
resource. In this example, the identifier that is used (kEffectatmsRes
) is also used in the call to GetComponentResource
in Listing 4-1. This ensures that the right 'atms'
resource is read by QuickTime.
The second line contains the number of root atoms in the resource. Each 'atms'
resource contains a number of atoms. The number in the second line must contain a count of the number of first-level atoms in the resource.
The body of the 'atms'
resource consists of a number of atom declarations. Each declaration has a header that contains
the atom name
the atom ID
the number of children in the declaration
Each header is followed by the atom’s data, which is one or more typed values, such as a string
or a long
, or a set of child atoms.
Listing 4-11 shows an example atom that contains a single typed value as its data. Note that the value is a type followed by the data itself. The number of children of the atom is declared as noChildren
because the atom contains a typed value.
Listing 4-11 An example 'atms' atom declaration
kParameterTitleName, kParameterTitleID, noChildren, |
{ |
string { "Dimmer2 Effect Parameters" }; |
}; |
The Standard Information in an 'atms' Resource
The standard information stored in an 'atms'
resource is made up of three required atoms and five optional atoms.
The three required atoms are
Term |
Definition |
---|---|
|
a |
|
an |
|
a long integer containing the maximum number of sources that the effect can take. |
The five optional atoms are
Term |
Definition |
---|---|
|
an |
|
a |
|
a |
|
a |
|
a |
The Parameter Information in an 'atms' Resource
For each parameter of the effect, your 'atms'
resource must contain a set of atoms in the 'atms'
resource that describes that parameter. This description includes the name of the parameter, the type and range of values it can take, and hints on appropriate user interface element for setting this parameter.
A complete description of the information you need to provide for each parameter can be found in The Parameter Description Format.
For a basic parameter, there are five atoms that you should supply:
Term |
Definition |
---|---|
|
contains the type and ID of the parameter (an |
|
a long integer containing the type of the parameter, such as |
|
a set of typed values describing the range of values the parameter can take. The number and type of values you supply depend on the value of the |
|
two long integer values containing the behavior type, such as |
|
the default value of the parameter. Again, the type of this value will depend on the type of the |
An example of a basic parameter description is shown in Listing 4-12.
Listing 4-12 An example set of parameter description atoms
kParameterAtomTypeAndID, 101, noChildren, |
{ |
OSType { "sden" }; // atomType--the name of this parameter |
long { "1" }; // atomID--this is atom number 1 |
kAtomNotInterpolated; // atomFlags--this parameter cannot be tweened |
string { "Scratch Density" }; // atomName--the name of the parameter |
// as it will appear in the standard parameters dialog box |
}; |
kParameterDataType, 101, noChildren, |
{ |
kParameterTypeDataLong; // dataType--this parameter contains a |
// long value |
}; |
kParameterDataRange, 101, noChildren, |
{ |
long { "0" }; // minimumValue |
long { "25" }; // maximumValue |
long { "1" }; // scaleFactor--no scaling is applied to this |
// parameter |
long { "0" }; // precision--0 indicates that this parameter is |
// not a floating-point value |
}; |
kParameterDataBehavior, 101, noChildren, |
{ |
kParameterItemControl; // behaviorType--this parameters should be |
// represented by a slider |
long { "0" }; // behaviorFlags - no flags |
}; |
kParameterDataDefaultItem, 101, noChildren, |
{ |
long { "5" }; // the default value of the parameter |
}; |
The Parameter Description Format
The parameter description data structure is a QTAtomContainer
structure that, when filled out by the ImageCodecGetParameterList
call, contains a set of QTAtoms
for each parameter of the effect. These atoms define the base type of the parameter, the legal range of values that can be stored in it, and hints for displaying a user interface to set values for the parameter.
The atoms in a parameter description are described in the following sections. The order in which the atoms are stored in the QTAtomContainer
structure is important. Applications should present parameters to the user in the same order that they are contained in the parameter description.
Each of the atom types in a parameter description has a name; you will find constants for these in ImageCodec
.h
. You should use these constants when retrieving atoms from the data structure. The data stored in the atoms of the parameter description is structured, and the struct
definitions are given in the atom descriptions below.
Many of the atoms must be present to create a valid parameter description. Some are optional, as noted.
Parameter Atom Type and ID
This atom contains information about the type and ID of the parameter. The data is contained in the following structure:
typedef struct |
{ |
QTAtomType atomType; |
QTAtomID atomID; |
long atomFlags; |
Str255 atomName; |
} ParameterAtomTypeAndID; |
Term |
Definition |
---|---|
|
This field contains either a unique identifier for the parameter or the value |
|
This field contains the ID of this parameter. |
|
This field can contain one of the predefined values: |
|
The name of this parameter. This |
Special Description Types
If the parameter atom type and ID atom of a parameter description contains the constant kNoAtom
, this indicates that the value being described is not a parameter to the effect but is a group. Besides groups, two further special cases are covered in the following sections: enumeration lists and source counts.
Groups
It is sometimes useful to treat a set of parameters as a group. For example, you might want to label a group of parameters that jointly control something, align a group of controls, or enclose a set of parameters in a box. The grouping mechanism allows you to specify a set of parameters and the attributes that are applied to the group.
If the parameter data type and ID atom of a description contains child atoms, rather than data, it defines a group. A group is a set of related atoms, where the relationship amongst them can be based on attributes such as:
layout; for example, the group is a set of text labels that should be aligned.
spatial; for example, the items in the group should be placed side by side to optimize dialog box layout.
naming; the items in the group are related controls that should be displayed under a single heading in the dialog box.
usage; a pair of long integers may together specify a coordinate. In this case, they can be grouped together and the group’s parameter data usage atom set to
kParameterUsagePoint
.
Groups can be nested within one another as needed. Groups can optionally have a name, which allows your application to place grouped parameters within a panel or tabbed group under that name.
Listing 4-13 shows an example of a group, which in this case contains a single parameter description.
Listing 4-13 An example group atom from an 'atms' resource definition.
kParameterAtomTypeAndID, 100, noChildren, |
{ |
OSType { "none" }; // Use 'none' as this is not a real parameter |
long { "0" }; |
kAtomNoFlags; |
string { "" }; |
}; |
kParameterDataBehavior, 100, noChildren, |
{ |
kParameterItemGroupDivider; // Use a divider to separate this group |
kGroupNoFlags; |
}; |
kParameterDataType, 100, 1*5, // 1 parameter * 5 atoms to describe each |
//parameter |
{ |
}; |
kParameterAtomTypeAndID, 3, noChildren, |
{ |
OSType { "pMul" }; |
long { "1" }; |
kAtomNotInterpolated; |
string { "Pre-multiply color" }; |
}; |
kParameterDataType, 3, noChildren, |
{ |
kParameterTypeDataRGBValue; |
}; |
kParameterDataRange, 3, noChildren, |
{ |
short { "0" }; |
short { "0" }; |
short { "0" }; |
short { "65535" }; |
short { "65535" }; |
short { "65535" }; |
}; |
kParameterDataBehavior, 3, noChildren, |
{ |
kParameterItemColorPicker; |
long { "0" }; |
}; |
kParameterDataDefaultItem, 3, noChildren, |
{ |
short { "65535" }; |
short { "65535" }; |
short { "65535" }; |
}; |
Enumeration Lists
When an enumerated type is required for a parameter value, a new enumeration list is placed directly into the root atom container. Enumeration lists are arrays of name-and-value pairings in the following format:
typedef struct |
{ |
long value; |
Str255 name; |
} EnumValuePair; |
typedef struct |
{ |
long enumCount; // number of enumeration items to follow |
EnumValuePair values[1]; // values and names for them |
} EnumListRecord; |
The type of an enumeration list atom is kParameterEnumList
('enum'
). Listing 4-14 shows an enumeration list that contains three elements.
Listing 4-14 An example enumeration list from an 'atms' resource definition
kParameterEnumList, 1, noChildren, |
{ |
long { "3" }; // No of elements in the enum |
long {"1"}; string { "Straight Alpha" }; |
long {"2"}; string { "Pre-multiply Alpha" }; |
long {"3"}; string { "Reverse Alpha" }; |
}; |
Source Count
The source count atom (kParameterSourceCountName
, 'srcs'
) contains a single long integer value that defines the maximum number of sources that this effect can accept. The atom is always placed in the root atom container of the parameter description.
The source count atom is required.
Parameter Data Type
This atom defines the type of the data for this parameter. It contains data in the following structure:
typedef struct |
{ |
OSType dataType; |
} |
Term |
Definition |
---|---|
|
This field contains the type of the value that is stored in this parameter. This can be one of the following values: |
Term |
Definition |
---|---|
|
editable text item |
|
integer value |
|
enumerated lookup value |
|
fixed point value |
|
IEEE 64 bit floating point value |
|
bit field (Boolean) value |
|
RGBColor data |
|
reference to an image |
This atom is required.
Parameter Alternate Data Type
This atom defines a preferred data type for the parameter. If the system your application is running on does not support this preferred data type, the data type specified in the parameter data type atom will be used instead.
Use the alternate data type atom if you would prefer to use a data type that is not supported on all platforms, and use the parameter data type atom to specify a fall-back data type for systems that do not support your preferred data type.
For example, if the parameter alternate data type is kParameterTypeDataColorValue
, the parameter holds a value of type CMColor
on systems that have the ColorSync extension. On systems that do not have ColorSync, whatever is specified in the parameter data type (such as an
RGBValue
) is used instead.
This atom’s data is stored in a ParameterAlternateDataType
data structure, which in turn relies on the ParameterAlternateDataEntry
data structure.
typedef struct |
{ |
OSType dataType; // The type of the data |
QTAtomType alternateAtom; // The atom to use for alternate data |
} ParameterAlternateDataEntry; |
typedef struct |
{ |
long alternateCount; |
ParameterAlternateDataEntry alternates[]; |
} ParameterAlternateDataType; |
Term |
Definition |
---|---|
|
This field in the |
Term |
Definition |
---|---|
|
CM color data |
|
Cubic Beziers |
|
Nurbs |
The parameter alternate data type atom is optional.
Parameter Data Range
The Parameter Data Range atom defines the legal range of values that the parameter can take. It also defines a scaling constant that defines how the legal range of values can be translated into a range that is more suitable for display in a user interface. For example, a value with a range of 0-255 might be scaled as 0-100 for user input.
The atom’s data is structured as a RangeRecord
, defined below. The exact format of this data depends on the data type of the parameter being described.
// 'text' |
typedef struct |
{ |
long maxChars; // Maximum length of the string |
long maxLines; // Number of editing lines (typically 1) |
} StringRangeRecord; |
// 'long' |
typedef struct |
{ |
long minValue; // Minimum value the long can be |
long maxValue; // Maximum value the long can be |
long scaleValue; // Scaling constant |
long precisionDigits; // number of digits of precision |
// when editing via typing |
} LongRangeRecord; |
// 'enum' |
typedef struct |
{ |
long enumID; // The ID of the 'enum' atom in the |
// root container to search |
} EnumRangeRecord; |
// 'fixd' |
typedef struct |
{ |
Fixed minValue; // Minimum value the Fixed can be |
Fixed maxValue; // Maximum value the Fixed can be |
Fixed scaleValue; // Scaling constant |
long precisionDigits; // number of digits of precision |
// when editing via typing |
} FixedRangeRecord; |
// 'doub' |
typedef struct |
{ |
QTFloatDouble minValue; // Minimum value of parameter |
QTFloatDouble maxValue; // Maximum value of parameter |
QTFloatDouble scaleValue; // Scaling constant |
long precisionDigits; // number of digits of precision |
// when editing via typing |
} DoubleRangeRecord; |
// 'bool' |
typedef struct |
{ |
long maskValue; // value to mask on/off to set/clear the |
// boolean |
} BooleanRangeRecord; |
// 'rgb ' |
typedef struct |
{ |
RGBColor minColor; // Minimum value the RGBColor can be |
RGBColor maxColor; // Maximum value the RGBColor can be |
} RGBRangeRecord; |
// The RangeRecord data structure is the union of all of the above |
typedef struct |
{ |
union |
{ |
LongRangeRecord longRange; |
EnumRangeRecord enumRange; |
FixedRangeRecord fixedRange; |
DoubleRangeRecord doubleRange; |
StringRangeRecord stringRange; |
BooleanRangeRecord booleanRange; |
RGBRangeRecord rgbRange; |
} u; |
} RangeRecord; |
The minValue
and maxValue
fields of the DoubleRangeRecord
data structure can take, in addition to an actual QTFloatDouble
value, the following predefined values:
kNoMinimumDouble
; ignore the minimum valuekNoMaximumDouble
; ignore the maximum valuekNoScaleDouble
; don’t perform any scaling of value
The minValue
and MaxValue
fields of the LongRangeRecord
data structure can take, in addition to an actual long integer value, the following predefined values:
kNoMinimumLongFixed
; ignore minimum valuekNoMaximumLongFixed
; ignore maximum valuekNoScaleLongFixed
; don’t perform any scaling of valuekNoPrecision
; allow as many digits as format
The Parameter Data Range atom is required, except for group descriptions.
Parameter Data Behavior
The Parameter Data Behavior atom contains user interface hints that suggest to the client application how a parameter should be displayed.
typedef struct |
{ |
QTAtomID groupID; |
long controlValue; |
} ControlBehaviors; |
typedef struct |
{ |
OSType behaviorType; |
long behaviorFlags; |
union |
{ |
ControlBehaviorscontrols; |
} u; |
} ParameterDataBehavior; |
Term |
Definition |
---|---|
|
This field contains a value that specifies a user interface for editing the parameter's value. This field should contain one of the following pre-defined values: |
Term |
Definition |
---|---|
|
the parameter should be edited using an edit text field. |
|
the parameter should be edited using an edit text field that only accepts numerical entries. |
|
the parameter should be edited using an edit text field that accepts floating-point numerical entries. |
|
the parameter should be edited using a pop-up menu. This data behavior should only be used with parameters whose data type is |
|
the parameter should be edited using a group of radio buttons. This data behavior should only be used with parameters whose data type is |
|
the parameter should be edited using a checkbox This data behavior should only be used with parameters whose data type is |
|
the parameter should be edited using a standard control appropriate to the data type of the parameter. For parameters that accept a scalar value, such as a |
|
a horizontal line is drawn in above the control that manipulates this parameter's value. |
|
a rectangle is drawn around the control that manipulates this parameter's value. |
|
the parameter should be edited using a color swatch and picker. |
|
start of a new group of items. |
|
the parameter's name is displayed as a static text field. |
|
the parameter should be edited as an image that accepts drag and drop entry of new images. |
|
the parameter should be edited as a path display that allows the user to drag out a new path. |
|
This field can take one or more of the following values: |
Flag |
Definition |
---|---|
|
no options for graphics. |
|
any lines or rectangles that are drawn have a grayscale appearance. If this option is not set, lines and rectangles are drawn in black. |
|
no options for the group. |
|
the controls in the group are aligned. |
|
the controls in the group are surrounded with a box. |
|
display the controls in the group in a matrix, if such an arrangement is possible. |
|
do not display the name of the group. |
The behaviorFlags
values allow you to optionally show or hide a group depending on the value entered into a parameter. This allows you to express simple conditionals within a standard parameters dialog box. For example, you may want a pop-up menu with a set of fixed options, and an 'Others...' option; if the user chooses 'others', a text edit field is enabled to allow users to enter their own value.
To do this, you can use the kDisableWhenLessThan
flag to specify that the group containing the text control is disabled when the user chooses any value in the pop-up menu that is less than the last, 'Others...' option.
The following flags are available to control selective disabling of groups. For each of these flags, the ID of the group to be disabled is stored in the groupID
field of the controls
data structure. The value that is used in the comparison operation is stored in the controlValue
field of the controls
data structure.
Flag |
Definition |
---|---|
|
When the value chosen for this parameter is not equal to |
|
When the value chosen for this parameter is equal to |
|
When the value chosen for this parameter is less than the |
|
When the value chosen for this parameter is greater than the |
The parameter data behavior atom is required.
Parameter Data Usage
The parameter data usage atom defines the intended use of the data in the parameter. This information can be used by your application to provide a more appropriate user interface for a parameter or group of parameters. For example, if your application knows that a set of four long integer values actually represent a rectangle, it can present a graphical display of the rectangle, rather than simply displaying four numeric input fields.
The data in this atom is stored in the following data structure:
typedef struct |
{ |
OSType usageType; |
} ParameterDataUsage; |
Term |
Definition |
---|---|
|
This field defines the actual use that a parameter or group of parameters. It can take one of the following values: |
Term |
Definition |
---|---|
|
The parameters in the group contain a set of pixels. |
|
The parameters in the group contain the top-left and bottom-right coordinates of a rectangle. |
|
The parameters in the group contain the coordinates of a point. |
|
The parameters in the group contain the X,Y,Z coordinates of a 3D point. |
|
The parameters in the group contain a 3x3 matrix of values. |
|
The parameter contains degrees. |
|
The parameter contains radians. |
|
The parameter contains a percentage. |
|
The parameter contains seconds. |
|
The parameter contains milliseconds. |
|
The parameter contains microseconds. |
The parameter data usage atom is optional.
Parameter Data Default Item
The parameter data default item atom contains the default value for the parameter. This value is stored in a QuickTime atom and can be copied directly into the parameter or into an effect description; an application does not need to understand the contents or format of the atom in order to do this.
The parameter data default item atom is required, except for group descriptions.
Tweening Parameters
An important property of effect parameters is that many can be tweened, and some must be tweened. Tweening is QuickTime’s general purpose interpolation mechanism (see QuickTime Media Types and Media Handlers Guide). This allows the value of the parameter to change as the effect executes.
For example, the slide effect built into QuickTime (see Slide) has an angle parameter. This controls the angle from which the second source will slide over the first during the execution of the effect. If this parameter contains a single value, the second source will slide over the first in a straight line from the selected angle. However, if the parameter contains two values, the angle will be interpolated between these values during the execution of the effect. This allows you to specify a curved slide effect.
In fact, any valid tween record can be specified as the parameter value, not just records containing pairs of values. The QuickTime tweening mechanism supports tween records that contain more than two values and that specify the interpolation algorithm used to produce intermediate values. However, the standard parameters dialog box allows only a pair of values to be entered, and the appropriate default interpolator is used. The standard parameter dialog box presents the user with a pair of values for parameters that must be tweened. Parameters that are optionally tweened, such as the angle for the slide effect, are set to a single value by default. In order to set an optionally-tweened parameter to a tweened value, the user must hold down the Option key when selecting the effect in the dialog box.
An application can provide its own user interface for entering multiple tween values for a parameter and choosing an appropriate tweener to perform interpolation, if required.
For more details on specifying which parameter values can contain tween values, see Parameter Atom Type and ID. For more details on supporting tweened parameters in your effect component, see Tweening Parameter Values.
Refer to The Parameter Description Format for a complete description of the possible parameter descriptions you can place in your 'atms'
resource.
Slide
kSlideTransitionType
('slid')
In a slide effect, source B slides onto the screen to cover source A. The angle from which source B enters the frame is stored in a parameter, with 0 degrees being the top of the screen.
The slide effect takes a maximum of two sources and has two parameters.
Use the descriptions below to help you understand what the parameters do. To learn how to use parameter atoms, see Adding Video Effects to a QuickTime Movie.
Name |
Code |
QTAtom Type |
Description |
---|---|---|---|
Percentage |
|
|
This parameter contains a tween. As the effect progresses, QuickTime renders the frame of the effect indicated by the tween's current value, as a percentage of the whole effect. For example, if the tween goes from 0 to 100, the effect renders completely; if the tween goes from 25 to 75, rendering begins 25% into the effect and terminates 75% through the effect. |
Slide angle |
|
|
The angle from which source B will enter the frame. This value is expressed in degrees, with |
Parameter Descriptions
Each effect component supplies a parameter description data structure that describes in detail the set of parameters that the effect has.
This section describes the parameter description format in detail. You need this information if you are writing an effect component. If you are writing an effect component, you should provide a parameter description as part of an 'atms'
resource (see Supplying Parameter Description Information for more details).
You may also need this information if you are writing an application that presents its own user interface for setting effect parameters. In this case, you will need to parse parameter descriptions to generate appropriate controls to set parameter values. Most applications can simply use the QTCreateStandardParameterDialog
function, and do not need to parse effect parameter descriptions.
Any software that uses an effect can request its parameter description. Typically, the parameter description is then passed to QTCreateStandardParameterDialog
or especially, ImageCodecCreateStandardParameterDialog
. These functions use the parameter description to display a user interface that allows users to choose the values of the parameters.
Your are free to use the information in an effect’s parameter description in other ways. For example, your application can use the default value atoms to construct an effect description.
Parameter descriptions are stored in a QTAtomContainer
structure, and an application retrieves an effect’s description by calling ImageCodecGetParameterList
. This function takes a component instance and returns the parameter description for that component.
The code shown in Listing 4-15 opens the component specified in the variable subType
. The code sets up the component description, then finds and opens the requested component. It then calls the ImageCodecGetParameterList
function to fill out the parameter description for this effect.
Listing 4-15 Opening the image decompressor component
{ |
// Set up a component description |
cd.componentType = 'imdc'; // Effects are image decompressor |
// components |
cd.componentSubType = subType; // This is the name of the effect |
//(e.g. 'smpt') |
cd.componentManufacturer = 0; |
cd.componentFlags = 0; |
cd.componentFlagsMask = 0; |
// Find the required component. If it can't be found, generate an |
// error |
if ((theComponent = FindNextComponent(theComponent, &cd))==0) |
{ |
err = paramErr; |
goto bail; |
} |
// Open the component |
gCompInstance = OpenComponent(theComponent); |
// Get the parameter description for the effect |
ImageCodecGetParameterList(gCompInstance, ¶meterDescription); |
} |
An application can parse the returned parameter description using the standard QuickTime APIs that query QTAtomContainer
data structures. This can be useful if you are writing an application that creates its own interface for users to customize effects.
This section describes the general format of the data returned in a parameter description.
Component-Defined Functions
This section defines the effect-specific functions that you may supply in your effect components. This section is only of interest to developers who are creating their own effects components; if you are writing an application that uses QuickTime video effects, you can skip this section.
The functions defined in this section are those called by the Component Manager through your component’s dispatch function (see What Effects Components Do).
These functions include
If you are using the sample effect component, you can use the default implementations of several of these functions in most circumstances.
MyEffectSetup
The Component Manager calls this function when a sequence of frames is about to be rendered.
ComponentResult MyEffectSetup ( |
EffectGlobals *glob, |
CodecDecompressParams *decompressParams); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
Information about the sequence that is about to be decompressed. |
This function is called immediately before a client application such as MoviePlayer calls your component to render a sequence of frames.
Your component should examine the capabilities
field of the decompressParams
data structure to ensure that it can meet the requirements for executing this sequence. In particular, it should check the bit depth and pixel format requirements of the sequence. If the sequence requires a bit depth and pixel format combination that your component does not support, this function should return the nearest supported combination in the decompressParams
-> capabilities
field. In this case, QuickTime will redirect all source and destination bitmaps through offscreen graphics worlds that have the bit depth and pixel format characteristics that you specify.
MyEffectBegin
The Component Manager calls this function to request that your component prepare to render a single frame of its effect.
ComponentResult MyEffectBegin ( |
EffectGlobals *glob, |
CodecDecompressParams *decompressParams, |
EffectsFrameParamsPtr effect); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
Information about the current sequence of frames. |
|
The parameters describing this frame. |
This function is called immediately before your MyEffectRenderFrame
function. Your MyEffectBegin
function should ensure that the information it holds about the current source and destination buffers and the parameter values for the effect are valid. If any of these have changed since the last call to MyEffectBegin
, the new values should be read from the appropriate data structures.
This function is guaranteed to be called synchronously. In particular, this means you can allocate and move memory, and can call functions that allocate or move memory.
MyEffectRenderFrame
The Component Manager calls this function to request that your component render a single frame of its effect.
ComponentResult MyEffectRenderFrame ( |
EffectGlobals *glob, |
EffectsFrameParamsPtr effect); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
The parameters describing this frame. |
This function is called by a client application when your effect component needs to render a single frame of your effect. This function contains the implementation of your effect.
MyEffectCancel
The Component Manager calls this function to stop processing of the current effect.
ComponentResult MyEffectCancel ( |
EffectGlobals *glob, |
EffectsFrameParamsPtr effect); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
The parameters describing this frame. |
This function is called by a client application (which may be QuickTime) to halt the rendering of the current sequence of frames before the last frame has been rendered. If your component is running synchronously, it should simply return noErr
; no further calls to your MyEffectRenderFrame
function will be made for this sequence.
If your component is running asynchronously, this function should dequeue all outstanding render frame requests, then return noErr
.
MyEffectGetCodecInfo
The Component Manager calls this function to request information about the component.
ComponentResult MyEffectGetCodecInfo ( |
EffectGlobals *glob, |
CodecInfo *info); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
A pointer to the data structure that will contain the codec information. |
This function is called by a client application (which may be QuickTime) to request information about your effect component. Your function should fill out the CodecInfo
data structure passed to it. You can use the GetComponentResource
function to retrieve a 'cdci'
resource that stores this information if you have provided one in your component.
|
0 |
The function successfully filled out the info field. |
---|---|---|
|
-50 |
Your function should return this value if the info parameter contains nil. |
MyEffectGetParameterListHandle
The Component Manager calls this function to request a parameter description for this component.
ComponentResult MyEffectGetParameterListHandle ( |
EffectGlobals *glob, |
Handle theHandle); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
A pointer to a handle that will contain the parameter description of this effect. |
This function is called by a client application (which may be QuickTime) to request a parameter description for your effect. This function can use the GetComponentResource
function to retrieve an'atms'
resource that stores this information if you have provided one in your component.
MyEffectGetSpeed
The Component Manager calls this function to request information about the rendering speed of this effect component.
long MyEffectGetSpeed ( |
EffectGlobals *glob, |
QTAtomContainer parameters, |
Fixed *pFPS) |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
The current parameter values for this effect. |
|
A pointer to a |
This function is called by a client application (which may be QuickTime) to request information about the rendering speed of your effect. This function should return a Fixed
value in FPS, which represents the rendering speed in frames-per-second of the effect.
If your effect can render in real time, it should return a value of effectIsRealtime
. Otherwise, you should return an estimate of the number of frames your effect can render per second. Because rendering speeds are hardware-dependent, effect authors can choose to measure actual rendering speeds in this function. Alternatively, effect authors can choose to return a single value for all hardware configurations, estimating the value for a reference hardware platform.
Apple recommends that the values returned are rounded down to the nearest common frames-per-second value, such as 15, 24 or 30.
MyEffectValidateParameters
If your effect implements this optional function, the Component Manager calls it whenever the user changes a parameter value in the standard parameter dialog box, or attempts to dismiss the dialog.
ComponentResult MyEffectValidateParameters ( |
EffectGlobals *glob QTAtomContainer parameters, |
QTParameterValidationOptions validationFlags, |
StringPtr errorString); |
Term |
Definition |
---|---|
|
A pointer to the effect's global data structure. |
|
The current parameter values for this effect. |
|
Flags that indicate whether a parameter value has changed or the user is dismissing the standard parameter dialog box. |
|
A |
This optional function is called by a client application (which may be QuickTime) when your effect’s standard parameter dialog box is being displayed. It can be called in two circumstances: if the user changes a parameter value in the dialog box; or if the user dismisses the dialog box by clicking OK.
The purpose of this function is to allow your effect to validate its parameters. The current parameter values are passed to the effect in parameters
. If all of these values are valid, this function should return noErr
. Otherwise, you should return a paramErr
and put an explanatory message in the errorString
parameter.
Copyright © 2005, 2009 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2009-06-01