Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Using Tween Components
This chapter describes how to create tween containers that the tween media handler uses. Several utility routines are also discussed.
The tween media handler uses tween components to perform tween operations (other than simple ones built into QuickTime). The components use containers to define their tween operations, and the containers are constructed from QT atoms. The tween media handler is discussed in Tween Media Handlers.
Creating a Single Tween Container
To create a single tween container, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween. AkTweenEntry
atom contains the atoms that define the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom that contains the data for the tween into thekTweenEntry
atom.
Listing 7-1 shows how to create a single kTweenTypeLong
tween container that a component could use to interpolate two long integers.
Listing 7-1 Creating a single kTweenTypeLong tween container
QTAtomContainer container = nil; |
long tweenDataLong[2]; |
QTAtomType tweenType; |
QTAtom tweenAtom; |
tweenDataLong[0] = EndianU32_NtoB(512); |
tweenDataLong[1] = EndianU32_NtoB(0); |
// create a new atom container |
QTNewAtomContainer (&container); |
// create the parent tween entry atom |
tweenType = kTweenTypeLong; |
QTInsertChild (container, kParentAtomIsContainer, kTweenEntry, 1, 0, 0, |
nil, &tweenAtom); |
// add two child atoms to the tween entry atom -- |
// * the type atom, kTweenType |
QTInsertChild (container, tweenAtom, kTweenType, 1, 0, |
sizeof(tweenType), &tweenType, nil); |
// * the data atom, kTweenData |
QTInsertChild (container, tweenAtom, kTweenData, 1, 0, |
sizeof(long) * 2, tweenDataLong, nil); |
Using Path Tween Components
The following sections describe how to use a variety of path tween components. All path tween operations have as input a QuickTime vector data stream for a path. Path tweeners interpret their time input in one of these ways:
As a percentage of path’s length. For these types, either a point on the path or a
MatrixRecord
data structure is returned.As a function. One operation returns the
y
value of the point on the path with a givenx
value, and the other returns thex
value of the point on the path with a giveny
value.
If the kTweenReturnDelta
flag (in an optional kTweenFlags
atom in the kTweenEntry
atom) is set, a path tween returns the change in value from the last time it was invoked. If the flag is not set, or if the component has not previously been invoked, the component returns the normal result for the tween.
Using a List Tween Component
To use a list tween component (of type kTweenTypeAtomList
), do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom. Unlike the previous example, thiskTweenData
atom is not a leaf atom.Insert a
kListElementType
atom that specifies the atom type of the list entries into thekTweenData
atom. The list entries must be leaf atoms.Insert leaf atoms of the type specified by the
kListElementType
atom into thekTweenData
atom.
The duration of the tween operation is divided by the number of leaf atoms of the specified type. For time points within the first time division (from the start of the duration up to an including the time (total time / number of atoms )), the data for the first leaf atom is returned; for the second time division, the data for the second leaf atom is returned; and so on.
Listing 7-2 shows how to create a list tween.
Listing 7-2 Creating a kTweenTypeAtomList tween container
QTAtomContainer container = nil; |
long tweenDataLong[2]; |
QTAtomType tweenType; |
QTAtom tweenAtom; |
tweenDataLong[0] = EndianU32_NtoB(512); |
tweenDataLong[1] = EndianU32_NtoB(0); |
// create a new atom container to hold the sample |
QTNewAtomContainer (&container); |
// create the parent tween entry atom |
tweenType = EndianU32_NtoB(kTweenTypeLong); |
QTInsertChild (container, kParentAtomIsContainer, kTweenEntry, 1, 0, 0, |
nil, &tweenAtom); |
// add two child atoms to the tween entry atom -- |
// * the type atom, kTweenType |
QTInsertChild (container, tweenAtom, kTweenType, 1, 0, |
sizeof(tweenType), &tweenType, nil); |
// * the data atom, kTweenData |
QTInsertChild (container, tweenAtom, kTweenData, 1, 0, |
sizeof(short) * 2, tweenDataLong, nil); |
OSErr err = noErr; |
QTTweener tween; |
QTAtomContainer container = nil, listContainer = nil; |
OSType tweenerType; |
TimeValue offset, duration, tweenTime; |
Handle result; |
QTAtom tweenAtom; |
tweenerType = EndianU32_NtoB(kTweenTypeAtomList); |
offset = 0; |
duration = 3; |
err = QTNewAtomContainer( &container ); |
if ( err ) goto bail; |
err = AddTweenAtom( container, kParentAtomIsContainer, 1, tweenerType, |
offset, duration, 0, 0, nil, &tweenAtom ); |
if ( err ) goto bail; |
listContainer = CreateSampleAtomListTweenData( 1 ); |
if ( listContainer == nil ) { err = memFullErr; goto bail; } |
err = AddDataAtom( container, tweenAtom, 1, 0, nil, |
listContainer, 0, nil ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
result = NewHandle( 0 ); |
if ( err = MemError() ) goto bail; |
// exercise the AtomListTweener |
for ( tweenTime = 1; tweenTime <= duration; tweenTime += 1 ) { |
long pictureID; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
// the pictureID from the atomDataList corresponding to tweenTime |
pictureID = *(long *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( listContainer ) QTDisposeAtomContainer( listContainer ); |
if ( result ) DisposeHandle( result ); |
return err; |
} |
Utility Routines
The examples in the next sections use several utility routines to modularize their code. These routines are the following:
AddTweenAtom
adds an atom of typekTweenEntry
plus its standard child atoms (other than thekTweenDataAtom
) to a container.AddDataAtom
adds a data atom with an ID ofdataAtomID
as a child atom oftweenAtom
.AddSequenceTweenAtom
adds akTweenEntry
atom for a sequenced tween.AddSequenceElement
adds a leaf atom of typekTweenSequenceElement
to a container.
AddTweenAtom
Listing 7-3 shows AddTweenAtom
, a routine that adds an atom of type kTweenEntry
plus its standard child atoms (other than the kTweenDataAtom
) to a container. Only the tweenAtomID
and tweenerType
parameters are required; you may pass 0 for the other parameters to avoid using them.
The minOutput
and maxOutput
atom parameters are necessary only if the tweener is being used as an interpolator. Passing a tweenAtomID
value of 0 means that the routine can assign any unique ID.
If you use AddTweenAtom
to add a nonsequenced tween entry to a container, the kTweenEntry
atom it creates is the atom you pass to QTNewTween
and the sequenceAtom you pass in may be kParentAtomIsContainerTween
Components and Tween Media. In this case the tweenAtomID
value should be 1.
If you use AddTweenAtom
to add a sequenced tween entry to a container, the newSequenceAtom
returned by AddSequenceTweenAtom
is its sequenceAtom
parameter and will also be the atom you pass to QTNewTween
. Note that in most cases a sequenced tween contains only one tween entry but may contain multiple data atoms.
All tween atoms within same sequenceAtom
must have same tween type. It is unlikely that you would want to change the duration or offset values within the same sequenceAtom
.
Listing 7-3 Utility routine AddTweenAtom
OSErr AddTweenAtom( QTAtomContainer container, QTAtom sequenceAtom, |
QTAtomID tweenAtomID, OSType tweenerType, TimeValue offset, |
TimeValue duration, Fixed minOutput, Fixed maxOutput, StringPtr name, |
QTAtom *newTweenAtom ) |
{ |
OSErr err = noErr; |
QTAtom tweenAtom = 0; |
if ( ! container ) { err = paramErr; goto bail; } |
err = QTInsertChild( container, sequenceAtom, kTweenEntry, |
tweenAtomID, 0, 0, nil, &tweenAtom ); |
if ( err ) goto bail; |
err = QTInsertChild( container, tweenAtom, kTweenType, 1, 1, |
sizeof(tweenerType), &tweenerType, nil ); |
if ( err ) goto bail; |
if ( offset ) { |
err = QTInsertChild( container, tweenAtom, kTweenStartOffset, 1, |
1, sizeof(offset), &offset, nil ); |
if ( err ) goto bail; |
} |
if ( duration ) { |
err = QTInsertChild( container, tweenAtom, kTweenDuration, 1, 1, |
sizeof(duration), &duration, nil ); |
if ( err ) goto bail; |
} |
// default minOutput is zero, so this is OK |
if ( minOutput ) { |
err = QTInsertChild( container, tweenAtom, kTweenOutputMin, 1, 1, |
sizeof(minOutput), &minOutput, nil ); |
if ( err ) goto bail; |
} |
if ( maxOutput ) { |
err = QTInsertChild( container, tweenAtom, kTweenOutputMax, 1, 1, |
sizeof(maxOutput), &maxOutput, nil ); |
if ( err ) goto bail; |
} |
if ( name ) { |
err = QTInsertChild( container, tweenAtom, kNameAtom, 1, 1, |
name[0] + 1, name, nil ); |
if ( err ) goto bail; |
} |
bail: |
if ( newTweenAtom ) |
*newTweenAtom = tweenAtom; |
return err; |
} |
AddDataAtom
Listing 7-4 shows AddDataAtom
, a routine that adds a data atom with an ID of dataAtomID
as a child atom of tweenAtom
. If dataSize
is nonzero, then leaf data is copied from dataPtr
to dataAtom
. Otherwise (if dataContainer
in not nilTween
Components and Tween Media), child atoms are copied from dataContainer
. If you wish to add the actual data by using another routine, you may pass 0 in dataSize
and nil
in both dataPtr
and dataContainer
.
You can associate a tweener to be used as an interpolator for each dataAtom
value. The interpolationTweenID
parameter specifies the ID of a kTweenEntry
atom that is a child of the newSequenceAtom
returned by AddSequenceTweenAtom
. If you specify an interpolation tweener, then the atTime
parameter of the DoTween
routine is first fed as an input to the interpolation tweener. The tweenResult
of the interpolation tweener becomes the atTime
parameter of the succeeding tweener. Note that the kTweenData
atom and the kTweenInterpolationID
atom have the same ID; this is how QuickTime groups them together.
For best performance, the output range of an interpolation tweener should be from 0 to the duration of the regular tweener. However, you may specify the minimum and maximum values that the interpolation tweener returns; this lets the tweener be shared. The minimum and maximum values are used to scale tweenResult
, and are added as child atoms of a kTweenEntry
in AddTweenAtom
.
Listing 7-4 Utility routine AddDataAtom
OSErr AddDataAtom( QTAtomContainer container, QTAtom tweenAtom, |
QTAtomID dataAtomID, long dataSize, Ptr dataPtr, |
QTAtomContainer dataContainer, QTAtomID interpolationTweenID, |
QTAtom *newDataAtom ) |
{ |
OSErr err = noErr; |
QTAtom dataAtom = 0; |
if ( (! container) || (dataAtomID == 0) || (dataSize && |
(dataContainer || !dataPtr)) ) { err = paramErr; goto bail; } |
err = QTInsertChild( container, tweenAtom, kTweenData, dataAtomID, 0, |
dataSize, dataPtr, &dataAtom ); |
if ( err ) goto bail; |
if ( dataSize ) { |
err = QTSetAtomData( container, dataAtom, dataSize, dataPtr ); |
if ( err ) goto bail; |
} |
else if ( dataContainer ) { |
err = QTInsertChildren( container, dataAtom, dataContainer ); |
if ( err ) goto bail; |
} |
if ( interpolationTweenID ) { |
err = QTInsertChild( container, tweenAtom, kTweenInterpolationID, |
dataAtomID, 0, sizeof(interpolationTweenID), |
&interpolationTweenID, nil ); |
if ( err ) goto bail; |
} |
bail: |
if ( newDataAtom ) |
*newDataAtom = dataAtom; |
return err; |
} |
AddSequenceTweenAtom
Listing 7-5 shows AddSequenceTweenAtom
, a routine that adds a kTweenEntry
atom for a sequenced tween. The newSequenceAtom
returned may be passed into the AddTweenAtom
and AddSequenceElement
routines as their sequenceAtom
parameter.
To create a nonsequenced tween, use AddTweenAtom
instead.
Listing 7-5 Utility routine AddSequenceTweenAtom
OSErr AddSequenceTweenAtom( QTAtomContainer container, QTAtom parentAtom, |
QTAtomID sequenceAtomID, QTAtom *newSequenceAtom ) |
{ |
if ( (! container) || (sequenceAtomID == 0) ) { return paramErr; } |
return QTInsertChild( container, parentAtom, kTweenEntry, |
sequenceAtomID, 0, 0, nil, newSequenceAtom ); |
} |
AddSequenceElement
Listing 7-6 shows AddSequenceElement
, a routine that adds a leaf atom of type kTweenSequenceElement
to a container. The sequenceAtom
that you pass in is the newSequenceAtom
parameter returned by AddSequenceTweenAtom
. Each time you call AddSequenceElement
, a sequence tween element is added to the end of the list of elements. The tween toolbox organizes the list in index order, with IDs ignored.
Each element has a duration that is some percentage of the tween’s duration. The element’s duration is its endPercent
value minus the previous element’s endPercent
value. For example, if you wanted three elements to last 0.25, 0.25, and 0.5 of the tween’s duration, then the elements' endPercent
values should be set to 0.25, 0.5, and 1. The elements tell the tween toolbox which tweenAtom
and dataAtom
to switch to.
The tweenAtomID
is the ID of a kTweenEntry
atom within the sequenceAtom
. The dataAtomID
is the ID of a kTweenData
atom. The kTweenData
atom is a child atom of the specified tweenAtom
. Usually you only need to create one tweenAtom
with multiple data atoms. Some tweener types (such as 3D tweeners) use child atoms of the tweenEntry
atom for initialization, so in these cases you usually create a tweenAtom
with one dataAtom
per sequence entry.
Listing 7-6 Utility routine AddSequenceElement
OSErr AddSequenceElement( QTAtomContainer container, QTAtom sequenceAtom, |
Fixed endPercent, QTAtomID tweenAtomID, QTAtomID dataAtomID, |
QTAtom *newSequenceElementAtom ) |
{ |
TweenSequenceEntryRecord entry; |
if ( (! container) || (endPercent > (1L<<16)) || (tweenAtomID == 0) |
|| (dataAtomID == 0) ){ return paramErr; } |
entry.endPercent = endPercent; |
entry.tweenAtomID = tweenAtomID; |
entry.dataAtomID = dataAtomID; |
// adds at end of list by index, with any unique atom id |
return QTInsertChild( container, sequenceAtom, kTweenSequenceElement, |
0, 0, sizeof(entry), &entry, newSequenceElementAtom ); |
} |
CreateSampleAtomListTweenData
Listing 7-7 shows the CreateSampleAtomListTweenData
routine.
Listing 7-7 Utility routine CreateSampleAtomListTweenData
QTAtomContainer CreateSampleAtomListTweenData( long whichOne ) |
{ |
OSErr err = noErr; |
QTAtomContainer atomListContainer; |
QTAtomType tweenAtomType; |
UInt32 elementDataType; |
UInt16 resourceID; |
err = QTNewAtomContainer( &atomListContainer ); |
if ( err ) goto bail; |
// kListElementDataType atom specifies the data type of the elements |
elementDataType = kTweenTypeShort; |
err = QTInsertChild( atomListContainer, kParentAtomIsContainer, |
kListElementDataType, 1, 1, |
sizeof(tweenAtomType), |
&tweenAtomType, nil ); |
// kListElementType atom tells which type of atoms to look for |
tweenAtomType = 'pcid'; |
err = QTInsertChild( atomListContainer, kParentAtomIsContainer, |
kListElementType, 1, 1, sizeof(tweenAtomType), |
&tweenAtomType, nil ); |
switch ( whichOne ) { |
case 1: |
resourceID = 1000; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 1, 1, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
resourceID = 1001; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 2, 2, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
resourceID = 1002; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 3, 3, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
break; |
case 2: |
resourceID = 1003; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 1, 1, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
resourceID = 1004; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 2, 2, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
resourceID = 1005; |
err = QTInsertChild( atomListContainer, |
kParentAtomIsContainer, 'pcid', 3, 3, |
sizeof(resourceID), &resourceID, nil ); |
if ( err ) goto bail; |
break; |
} |
bail: |
if ( err && atomListContainer ) |
{ QTDisposeAtomContainer( atomListContainer ); |
atomListContainer = nil; } |
return atomListContainer; |
} |
Using a Polygon Tween Component
A polygon tweener maps a four-sided polygon, such as the boundary of a sprite or track, into another. It can be used to create perspective effects, in which the shape of the destination polygon changes over time. The range of polygons into which the source polygon is mapped is defined by two additional four-sided polygons, which are interpolated to specify a destination polygon for any time point in the tween duration.
To use a polygon tween component (of type kTweenTypePolygonTween
Components and Tween Media), do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.
The data is an array of 27 fixed-point values (Fixed[27]Tween
Components and Tween Media) that specifies the three four-sided polygons. Each polygon is specified by 9 consecutive array elements. The first element is each set of 9 contains the number of points used to specify the polygon; this value is coerced to a long integer, and it must always be 4 after coercion. The following 8 values in each set of nine are four x
, y
pairs that specify the corners of the polygon.
The first set of 9 elements specifies the dimensions of a sprite or track to be mapped. For example, if the object is a sprite, the four points are (0,0), (spriteWidth, 0 ), (spriteWidth, spriteHeight), (0, spriteHeight). The next set of 9 elements specifies the initial polygon into which the sprite or track is mapped. The next set of 9 elements specifies the final polygon into which the sprite or track is mapped.
The output is a MatrixRecord
data structure that you use to map the sprite or track into a four-sided polygon.
Listing 7-8 shows how to create a polygon tween.
Listing 7-8 Creating a polygon tween container
OSErr CreateSamplePolygonTweenContainer( QTAtomContainer container, |
TimeValue duration, QTAtom *newTweenAtom ) |
{ |
OSErr err = noErr; |
TimeValue offset; |
Handle thePolygonData = nil; |
QTAtom tweenAtom; |
err = QTRemoveChildren( container, kParentAtomIsContainer ); |
if ( err ) goto bail; |
offset = 0; |
err = AddTweenAtom( container, kParentAtomIsContainer, 1, |
kTweenTypePolygon, offset, duration, 0, 0, |
nil, &tweenAtom ); |
if ( err ) goto bail; |
thePolygonData = CreateSamplePolygonData(); |
if ( thePolygonData == nil ) { err = memFullErr; goto bail; } |
HLock( thePolygonData ); |
err = AddDataAtom( container, tweenAtom, 1, |
GetHandleSize( thePolygonData ), |
*thePolygonData, nil, 0, nil ); |
if ( err ) goto bail; |
bail: |
if ( thePolygonData ) DisposeHandle( thePolygonData ); |
if ( newTweenAtom ) *newTweenAtom = tweenAtom; |
return err; |
} |
Handle CreateSamplePolygonData( void ) |
{ |
OSErr err = noErr; |
Handle polygonData; |
Fixed *poly; |
polygonData = NewHandle( 27 * sizeof(Fixed) ); |
if ( polygonData == nil ) { err = memFullErr; goto bail; } |
poly = (Fixed *)*polygonData; |
poly[0] = EndianU32_NtoB(4); // source dimensions |
poly[1] = EndianU32_NtoB(Long2Fix( 0 )); |
poly[2] = EndianU32_NtoB(Long2Fix( 0 )); |
poly[3] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[4] = EndianU32_NtoB(Long2Fix( 0 )); |
poly[5] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[6] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[7] = EndianU32_NtoB(Long2Fix( 0 )); |
poly[8] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[9] = EndianU32_NtoB(4); // tween from polygon |
poly[10] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[11] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[12] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[13] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[14] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[15] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[16] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[17] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[18] = EndianU32_NtoB(4); // tween to polygon |
poly[19] = EndianU32_NtoB(Long2Fix( 140 )); |
poly[20] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[21] = EndianU32_NtoB(Long2Fix( 160 )); |
poly[22] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[23] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[24] = EndianU32_NtoB(Long2Fix( 200 )); |
poly[25] = EndianU32_NtoB(Long2Fix( 100 )); |
poly[26] = EndianU32_NtoB(Long2Fix( 200 )); |
bail: |
return polygonData; |
} |
Specifying an Offset for a Tween Operation
You can start a tween operation after a tween media sample begins by including an optional kTweenStartOffset
atom in the kTweenEntry
atom for the tween. This atom specifies a time interval, beginning at the start of the tween media sample, after which the tween operation begins. If this atom is not included, the tween operation begins at the start of the tween media sample.
Specifying a Duration for a Tween
You can specify the duration of a tween operation by including an optional kTweenDuration
atom in the kTweenEntry
atom for the tween. When a QuickTime movie includes a tween track, the time units for the duration are those of the tween track’s time scale. If a tween component is used outside of a movie, the application using the tween data determines how the duration value and values returned by the component are interpreted.
Creating a Tween Sequence
Single Tweens and Tween Sequences discussed tween sequences, in which different tween operations of the same type may be applied sequentially. The type kTweenSequenceElement
specifies an entry in a tween sequence. Its parent is the tween QT atom container (which you specify with the constant kParentAtomIsContainerTween
Components and Tween Media).
The ID of a kTweenSequenceElement
atom must be unique among the kTweenSequenceElement
atoms in the same QT atom container. The index of a kTweenSequenceElement
atom specifies its order in the sequence; the first entry in the sequence has the index 1, the second 2, and so on.
This atom is a leaf atom. The data type of its data is TweenSequenceEntryRecord
, a data structure that contains the following fields:
endPercent
, a value of typeFixed
that specifies the point in the duration of the tween media sample at which the sequence entry ends. This is expressed as a fraction; for example, if the value is 0.75, the sequence entry ends after three-quarters of the total duration of the tween media sample has elapsed. The sequence entry begins after the end of the previous sequence entry or, for the first entry in the sequence, at the beginning of the tween media sample.tweenAtomID
, a value of typeQTAtomID
that specifies thekTweenEntry
atom containing the tween for the sequence element. ThekTweenEntry
atom and thekTweenSequenceElement
atom must both be child atoms of the same tween QT atom container.dataAtomID
, a value of typeQTAtomID
that specifies thekTweenData
atom containing the data for the tween. This atom must be a child atom of the atom specified by thetweenAtomID
field.
Listing 7-9 shows how to create a tween sequence.
Listing 7-9 Creating a tween sequence
OSErr CreateSampleSequencedTweenContainer( QTAtomContainer container, |
TimeValue duration, QTAtom *newTweenAtom ) |
{ |
OSErr err = noErr; |
QTAtomContainer dataContainer = nil; |
OSType tweenerType; |
QTAtom sequenceAtom, tweenAtom; |
TimeValue offset; |
Handle result; |
QTAtomID tweenAtomID, dataAtomID; |
Fixed endPercent; |
err = QTRemoveChildren( container, kParentAtomIsContainer ); |
if ( err ) goto bail; |
tweenerType = kTweenTypeAtomList; |
offset = 0; |
err = AddSequenceTweenAtom( container, kParentAtomIsContainer, |
1, &sequenceAtom ); |
if ( err ) goto bail; |
offset = 0; |
err = AddTweenAtom( container, sequenceAtom, 1, tweenerType, offset, |
duration, 0, 0, nil, &tweenAtom ); |
if ( err ) goto bail; |
// add first data atom (id 1) to tween atom |
dataAtomID = 1; |
dataContainer = CreateSampleAtomListTweenData( dataAtomID ); |
if ( ! dataContainer ) { err = memFullErr; goto bail; } |
err = AddDataAtom( container, tweenAtom, dataAtomID, 0, nil, |
dataContainer, 0, nil ); |
if ( err ) goto bail; |
QTDisposeAtomContainer( dataContainer ); |
// add second data atom (id 2) to tween atom |
dataAtomID = 2; |
dataContainer = CreateSampleAtomListTweenData( dataAtomID ); |
if ( ! dataContainer ) { err = memFullErr; goto bail; } |
err = AddDataAtom( container, tweenAtom, dataAtomID, 0, nil, |
dataContainer, 0, nil ); |
if ( err ) goto bail; |
QTDisposeAtomContainer( dataContainer ); |
// now create a sequence with four elements; the first three are data |
// atom 1, the last is data atom 2 |
endPercent = FixDiv( Long2Fix(25), Long2Fix(100) ); |
tweenAtomID = 1; |
dataAtomID = 1; |
err = AddSequenceElement( container, sequenceAtom, endPercent, |
tweenAtomID, dataAtomID, nil ); |
if ( err ) goto bail; |
endPercent = FixDiv( Long2Fix(50), Long2Fix(100) ); |
tweenAtomID = 1; |
dataAtomID = 1; |
err = AddSequenceElement( container, sequenceAtom, endPercent, |
tweenAtomID, dataAtomID, nil ); |
if ( err ) goto bail; |
endPercent = FixDiv( Long2Fix(75), Long2Fix(100) ); |
tweenAtomID = 1; |
dataAtomID = 1; |
err = AddSequenceElement( container, sequenceAtom, endPercent, |
tweenAtomID, dataAtomID, nil ); |
if ( err ) goto bail; |
endPercent = FixDiv( Long2Fix(100), Long2Fix(100) ); |
tweenAtomID = 1; |
dataAtomID = 2; |
err = AddSequenceElement( container, sequenceAtom, endPercent, |
tweenAtomID, dataAtomID, nil ); |
if ( err ) goto bail; |
bail: |
if ( err ) { |
if ( container ) |
QTRemoveChildren( container, kParentAtomIsContainer ); |
*newTweenAtom = nil; |
} |
else |
*newTweenAtom = sequenceAtom; |
} |
Naming Tweens
You can use the kNameAtom
atom to store a string value containing a name (or any other information) in a tween container. This atom is not required and is not routinely accessed by QuickTime. It is available for use by your authoring tools or other software.
CreateSampleVectorData Utility
Listing 7-10 shows the CreateSampleVectorData
routine.
Listing 7-10 Utility routine CreateSampleVectorData
Handle CreateSampleVectorData( long whichOne ) |
{ |
OSErr err; |
Handle pathData = nil, vectorData = nil; |
ComponentInstance ci = nil; |
gxPoint aPoint; |
err = OpenADefaultComponent( decompressorComponentType, |
kVectorCodecType, &ci ); |
if ( err ) goto bail; |
err = CurveNewPath( ci, &pathData ); |
if ( err ) goto bail; |
if ( pathData == nil ) |
{ err = memFullErr; goto bail; } |
switch ( whichOne ) { |
case 1: |
aPoint.x = Long2Fix( 0 ); |
aPoint.y = Long2Fix( 100 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 0, false ); |
if ( err ) goto bail; |
aPoint.x = Long2Fix( 100 ); |
aPoint.y = Long2Fix( 0 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 1, false ); |
if ( err ) goto bail; |
aPoint.x = Long2Fix( 200 ); |
aPoint.y = Long2Fix( 100 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 2, false ); |
if ( err ) goto bail; |
aPoint.x = Long2Fix( 100 ); |
aPoint.y = Long2Fix( 200 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 3, false ); |
if ( err ) goto bail; |
break; |
case 2: |
aPoint.x = 0; |
aPoint.y = 100; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 0, false ); |
if ( err ) goto bail; |
aPoint.x = 100; |
aPoint.y = 0; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 1, false ); |
if ( err ) goto bail; |
aPoint.x = 200; |
aPoint.y = 100; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 2, false ); |
if ( err ) goto bail; |
aPoint.x = 100; |
aPoint.y = 200; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 3, false ); |
if ( err ) goto bail; |
break; |
case 3: |
aPoint.x = 0; |
aPoint.y = 0; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 0, true ); |
if ( err ) goto bail; |
aPoint.x = 200; |
aPoint.y = 50; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 1, true ); |
if ( err ) goto bail; |
aPoint.x = 400; |
aPoint.y = 400; |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 2, true ); |
if ( err ) goto bail; |
break; |
case 4: |
aPoint.x = Long2Fix( 0 ); |
aPoint.y = Long2Fix( 0 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 0, true ); |
if ( err ) goto bail; |
aPoint.x = Long2Fix( 200 ); |
aPoint.y = Long2Fix( 50 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 1, true ); |
if ( err ) goto bail; |
aPoint.x = Long2Fix( 400 ); |
aPoint.y = Long2Fix( 400 ); |
err = CurveInsertPointIntoPath( ci, &aPoint, pathData, |
0, 2, true ); |
if ( err ) goto bail; |
break; |
} |
err = CurveCreateVectorStream( ci, &vectorData ); |
if ( err ) goto bail; |
err = CurveAddPathAtomToVectorStream( ci, pathData, vectorData ); |
if ( err ) goto bail; |
err = CurveAddZeroAtomToVectorStream( ci, vectorData ); |
if ( err ) goto bail; |
bail: |
if ( pathData ) DisposeHandle( pathData ); |
if ( ci ) CloseComponent( ci ); |
if ( err != noErr ) { |
if ( vectorData ) { |
DisposeHandle( vectorData ); |
vectorData = nil; |
} |
} |
return vectorData; |
} |
CreateSamplePathTweenContainer Utility
Listing 7-11 shows how to use the CreateSamplePathTweenContainer
utility routine.
Listing 7-11 Utility routine CreateSamplePathTweenContainer
OSErr CreateSamplePathTweenContainer( QTAtomContainer container, |
OSType tweenerType, long whichSamplePath, Boolean returnDelta, |
TimeValue duration, Fixed initialRotation, QTAtom *newTweenAtom ) |
{ |
OSErr err = noErr; |
TimeValue offset; |
Handle thePathData = nil; |
QTAtom tweenAtom; |
err = QTRemoveChildren( container, kParentAtomIsContainer ); |
if ( err ) goto bail; |
offset = 0; |
err = AddTweenAtom( container, kParentAtomIsContainer, 1, |
tweenerType, offset, duration, 0, 0, nil, |
&tweenAtom ); |
if ( err ) goto bail; |
thePathData = CreateSampleVectorData( whichSamplePath ); |
if ( thePathData == nil ) { err = memFullErr; goto bail; } |
HLock( thePathData ); |
err = AddDataAtom( container, tweenAtom, 1, |
GetHandleSize( thePathData ), *thePathData, |
nil, 0, nil ); |
if ( err ) goto bail; |
if ( returnDelta ) { |
err = AddPathTweenFlags( container, tweenAtom, |
kTweenReturnDelta ); |
} |
if ( initialRotation ) |
QTInsertChild( container, tweenAtom, kInitialRotationAtom, |
1, 1, sizeof(initialRotation), &initialRotation, nil ); |
bail: |
if ( thePathData ) DisposeHandle( thePathData ); |
if ( newTweenAtom ) *newTweenAtom = tweenAtom; |
return err; |
Using a kTweenTypePathToMatrixTranslation Tween Component
To use a kTweenTypePathToMatrixTranslation
tween component, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-12 shows how to create a kTweenTypePathToMatrixTranslation
tween.
Listing 7-12 Creating a kTweenTypePathToMatrixTranslation tween container
OSErr err = noErr; |
TimeValue tweenTime, duration; |
Handle result = nil; |
QTAtomContainer container = nil; |
QTTweener tween = nil; |
QTAtom tweenAtom; |
duration = 8; |
result = NewHandle( 0 ); |
if ( err = MemError() ) goto bail; |
err = QTNewAtomContainer( &container ); |
if ( err ) goto bail; |
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToMatrixTranslation, 1, |
false, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
MatrixRecord absoluteMatrix; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absoluteMatrix = *(MatrixRecord *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Listing 7-13 shows how to create a kTweenTypePathToMatrixTranslation
tween in which the the kTweenReturnDelta
flag is set.
Listing 7-13 Creating a kTweenTypePathToMatrixTranslation tween
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToMatrixTranslation, 1, |
true, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
MatrixRecord deltaMatrix; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
deltaMatrix = *(MatrixRecord *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Using a kTweenTypePathToFixedPoint Tween Component
To use a kTweenTypePathToFixedPoint
tween component, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-14 shows how to create a kTweenTypePathToFixedPoint
tween.
Listing 7-14 Creating a kTweenTypePathToFixedPoint tween container
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToFixedPoint, 2, false, |
duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
gxPoint absolutePoint; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absolutePoint = *(gxPoint *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Listing 7-15 shows how to create a kTweenTypePathToFixedPoint
tween in which the kTweenReturnDelta
flag is set.
Listing 7-15 Creating a kTweenTypePathToFixedPoint tween container
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToFixedPoint, 2, true, |
duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
gxPoint deltaPoint; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
deltaPoint = *(gxPoint *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Using a kTweenTypePathToMatrixRotation Tween Component
To use a kTweenTypePathToMatrixRotation
tween component, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-16 shows how to create a kTweenTypePathToMatrixRotation
tween.
Listing 7-16 Creating a kTweenTypePathToMatrixRotation tween container
// kTweenTypePathToMatrixRotation |
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToMatrixRotation, 1, false, |
duration, X2Fix(0.5), &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
MatrixRecord absoluteMatrix; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absoluteMatrix = *(MatrixRecord *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Using a kTweenTypePathToMatrixTranslationAndRotation Tween Component
To use a kTweenTypePathToMatrixTranslationAndRotation
tween component, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-17 shows how to create a kTweenTypePathToMatrixTranslationAndRotation
tween.
Listing 7-17 Creating a kTweenTypePathToMatrixTranslationAndRotation tween container
err = CreateSamplePathTweenContainer( container, |
kTweenTypePathToMatrixTranslationAndRotation, |
1, false, duration, X2Fix(0.5), &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
MatrixRecord absoluteMatrix; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absoluteMatrix = *(MatrixRecord *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Using a kTweenTypePathXtoY Tween Component
To use kTweenTypePathXtoY
tween components, either absolute or delta, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-18 shows how to create both kinds of kTweenTypePathXtoY
tweens.
Listing 7-18 Creating kTweenTypePathXtoY tweens container
// kTweenTypePathXtoY - normal |
err = CreateSamplePathTweenContainer( container, kTweenTypePathXtoY, 3, |
false, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
Fixed absoluteYvalue; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absoluteYvalue = *(Fixed *)*result; |
} |
err = QTDisposeTween( tween ); |
// kTweenTypePathXtoY - delta |
err = CreateSamplePathTweenContainer( container, kTweenTypePathXtoY, 3, |
true, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
Fixed deltaYalue; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
deltaYalue = *(Fixed *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Using a kTweenTypePathYtoX Tween Component
To use kTweenTypePathYtoX
tween components, either absolute or delta, do the following:
Create a QT atom container.
Insert a
kTweenEntry
atom into the QT atom container for the tween.Insert a
kTweenType
atom that specifies the tween type into thekTweenEntry
atom.Insert a
kTweenData
atom into thekTweenEntry
atom.Perform the tweening operation, using
QTDoTween
.
Listing 7-19 shows how to create both kinds of kTweenTypePathYtoX
tweens.
Listing 7-19 Creating kTweenTypePathYtoX tweens container
// kTweenTypePathYtoX - normal |
err = CreateSamplePathTweenContainer( container, kTweenTypePathYtoX, 4, |
false, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
Fixed absoluteXvalue; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
absoluteXvalue = *(Fixed *)*result; |
} |
err = QTDisposeTween( tween ); |
// kTweenTypePathYtoX - delta |
err = CreateSamplePathTweenContainer( container, kTweenTypePathYtoX, 4, |
true, duration, 0, &tweenAtom ); |
if ( err ) goto bail; |
err = QTNewTween( &tween, container, tweenAtom, duration ); |
if ( err ) goto bail; |
for ( tweenTime = 0; tweenTime <= duration; tweenTime++ ) { |
Fixed deltaXvalue; |
err = QTDoTween( tween, tweenTime, result, nil, nil, nil ); |
if ( err ) goto bail; |
deltaXvalue = *(Fixed *)*result; |
} |
err = QTDisposeTween( tween ); |
bail: |
if ( container ) QTDisposeAtomContainer( container ); |
if ( result ) DisposeHandle( result ); |
Copyright © 2005, 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-06-04