Creating a Tween Component

This chapter explains how to create a tween component for a new data type, a new interpolation algorithm, or both. Before reading this section, you should be familiar with how to create components.

The following example illustrates a tween component that interpolates values for short integers. Because QuickTime handles this tween type (kTweenTypeShortTween Components and Tween Media) for you, you do not need to implement a component to handle interpolation of short integers yourself.

Initializing the Tween Component

Your tween component must process kTweenerInitializeSelect requests from the Component Manager. Listing 9-1 shows a function, TweenerInitialize, for processing this request. In this example, the function simply returns. In a more complex example, the function might allocate storage to be used when generating a tween media value.

Listing 9-1  Function that initializes a tween component

pascal ComponentResult TweenerInitialize (
                                             TweenerComponent tc,
                                             QTAtomContainer container,
                                             QTAtom tweenAtom,
                                             QTAtom dataAtom)
{
    return noErr;
}

Generating Tween Media Values

Your tween component must process kTweenerDoTweenSelect requests from the Component Manager. Listing 9-2 shows a function, TweenDoTween, for processing this request. It takes short-integer values and performs the necessary interpolation.

Listing 9-2  Function that generates tween media values

pascal ComponentResult TweenDoTween (
                                         TweenerComponent tc,
                                         TweenRecord *tr)
{
    short       *data;
    short       tFrom, tTo, tValue;
    QTGetAtomDataPtr(tr->container, tr->dataAtom, nil, (Ptr *)&data);
    tFrom = data[0];
    tTo = data[1];
    tValue = tFrom + FixMul(tTo - tFrom, tr->percent);
    (tr->dataProc)((struct TweenRecord *)tr, &tValue,
        sizeof(tValue), 1, nil, nil, nil, nil);
    return noErr;
}

Resetting a Tween Component

Your tween component must process kTweenerResetSelect requests from the Component Manager. Listing 9-3 shows the TweenReset function, which resets the component. In this example, because TweenerInitialize does not allocate any storage, TweenerReset simply returns. In a more complex example, TweenerReset releases any storage allocated by TweenerInitialize and any storage allocated during the tween operation.

Listing 9-3  Function that resets a tween component

pascal ComponentResult TweenerReset (TweenerComponent tc)
{
    return noErr;
}

Creating an Interpolation Tween

This section discusses tween operations that modify other tween operations by feeding them artificial time values in place of real time. Listing 9-4 shows how to create an interpolation tween.

Listing 9-4  Creating an interpolation tween container

OSErr CreateSampleInterpolatedTweenContainer( QTAtomContainer container,
    TimeValue duration, QTAtom *newTweenAtom )
{
    OSErr           err = noErr;
    Handle          pathData = nil;
 
    err = QTRemoveChildren( container, kParentAtomIsContainer );
    if ( err )goto bail;
 
    err = CreateSampleLongTweenContainer( container, 0, duration,
                                            duration, newTweenAtom );
    if ( err ) goto bail;
    pathData = CreateSampleVectorData( 3 );
    if ( ! pathData ) { err = memFullErr; goto bail; }
    err = AddXtoYInterpolatorTweenerForDataSet( container, *newTweenAtom,
                                            *newTweenAtom, 1, pathData );
    if ( err ) goto bail;
bail:
    if ( pathData )DisposeHandle( pathData );
    return err;
}
 
OSErr AddXtoYInterpolatorTweenerForDataSet( QTAtomContainer container,
    QTAtom sequenceTweenAtom, QTAtom tweenAtom, QTAtomID dataSetID,
    Handle vectorCodecData )
{
    OSErr               err = noErr;
    QTAtomID            interpolationTweenID;
    QTAtom              dataSetAtom, interpolatorTweenAtom, durationAtom,
                        interpolatorIDAtom;
    TimeValue           duration;
    ComponentInstance   ci = nil;
    UInt8               saveState;
    gxPaths             *thePathData;
    long                dataSize, numPoints;
    gxPoint             firstPoint, lastPoint;
    Boolean             ptIsOnPath;
    Fixed               minOutput, maxOutput;
 
    if ( (! container) || (! dataSetID) || (! vectorCodecData) )
                                        { err = paramErr; goto bail; }
    saveState = HGetState( vectorCodecData );
    dataSetAtom = QTFindChildByID( container, tweenAtom, kTweenData,
                                    dataSetID, nil );
    if ( ! dataSetAtom ) { err = cannotFindAtomErr; goto bail; }
 
    // determine duration of tweenEntry so we can use the same duration
    // for the interpolator tween
    durationAtom = QTFindChildByIndex( container, tweenAtom,
                                        kTweenDuration, 1, nil );
    if ( ! durationAtom ) { err = cannotFindAtomErr; goto bail; }
 
    err = QTCopyAtomDataToPtr( container, durationAtom, false,
                                    sizeof(duration), &duration, nil );
    if ( err ) goto bail;
 
    // determine the minOutput and maxOutput values based for the given
    // vector codec data
    err = OpenADefaultComponent( decompressorComponentType,
                                    kVectorCodecType, &ci );
    if ( err ) goto bail;
 
    HLock( vectorCodecData );
 
    err = CurveGetAtomDataFromVectorStream ( ci, vectorCodecData,
                        kCurvePathAtom, &dataSize, (Ptr *)&thePathData );
    if ( err ) goto bail;
    err = CurveCountPointsInPath( ci, thePathData, 0,
                                    (unsigned long *)&numPoints );
    if ( err ) goto bail;
    err = CurveGetPathPoint( ci, thePathData, 0, 0, &firstPoint,
                                &ptIsOnPath );
    if ( err ) goto bail;
    err = CurveGetPathPoint( ci, thePathData, 0, numPoints - 1,
                                &lastPoint, &ptIsOnPath );
    if ( err ) goto bail;
    minOutput = firstPoint.x;
    maxOutput = lastPoint.x;
 
    // add interolator tween atom with any unique id
    err = AddTweenAtom( container, sequenceTweenAtom, 0,
                        kTweenTypePathXtoY, 0, duration, minOutput,
                        maxOutput, nil, &interpolatorTweenAtom );
    if ( err ) goto bail;
    // so what was that unique id?
    err = QTGetAtomTypeAndID( container, interpolatorTweenAtom, nil,
                                &interpolationTweenID );
    if ( err ) goto bail;
    err = AddDataAtom( container, interpolatorTweenAtom, 1,
                        GetHandleSize( vectorCodecData ),
                        *vectorCodecData, nil, 0, nil );
    if ( err ) goto bail;
 
    // finally, we need to reference this new interpolator tween
    interpolatorIDAtom  = QTFindChildByID( container, tweenAtom,
                            kTweenInterpolationID, dataSetID, nil );
    if ( ! interpolatorIDAtom ) {
        err = QTInsertChild( container, tweenAtom, kTweenInterpolationID,
                            dataSetID, 0, 0, nil, &interpolatorIDAtom );
        if ( err ) goto bail;
    }
    err = QTSetAtomData( container, interpolatorIDAtom,
                sizeof(interpolationTweenID), &interpolationTweenID );
    if ( err ) goto bail;
bail:
    if ( vectorCodecData )
        HSetState( vectorCodecData, saveState );
    return err;
}

To scale the output of an interpolation tween, you add the optional kTweenOutputMaxValue atom and kTweenOutputMinValue atom.