Sprite Media Handler

This chapter discusses the sprite media handler, which you can use to add a sprite animation track to a QuickTime movie. It makes use of the functionality provided by the sprite toolbox discussed in Chapter 2, QuickTime Sprites, Sprite Animation and Wired Movies, and provides routines for manipulating the sprites and images in a sprite track. If you are using the sprite media handler, you don’t need to use the toolbox API.

The chapter is divided into the following major sections:

Defining the Sprite Media Handler

The sprite media handler is a media handler that makes it possible to add a track containing a sprite animation to a QuickTime movie. The sprite media handler provides routines for manipulating the sprites and images in a sprite track.

The sprite media handler makes use of routines provided by the sprite toolbox. As with sprites created in a sprite world, sprites in a sprite track have properties that define their locations, images, and appearance. However, you create the sprite track and its sprites differently than you create the sprites in a sprite world.

A sprite track is defined by one or more key frame samples, each followed by any number of override samples. A key frame sample and its subsequent override samples define a scene in the sprite track. A key frame sample is a QT atom container that contains atoms defining the sprites in the scene and their initial properties. The override samples are other QT atom containers that contain atoms that modify sprite properties, thereby animating the sprites in the scene. A sprite track sample is a QT atom container structure. In addition to defining properties for individual sprites, you can also define properties that apply to an entire sprite track. Chapter 8, QuickTime Atoms and Atom Containers, provides an overview QT atoms and atom containers. For complete information, refer to the book QuickTime File Format, which is available at

http://developer.apple.com/documentation/Quicktime/QuickTime.html

A key frame sample also contains all of the images used by the sprites. This allows the sprites in a sprite track to share image data. The images consist of two parts, an image description handle (ImageDescriptionHandle) concatenated with compressed image data. The image description handle describes the compressed image. You can compress the image using any QuickTime codec.

Images are stored in a key frame sample by index; each sprite has an image index property (kSpritePropertyImageIndex) that specifies the sprite’s current image. All images assigned to a sprite must be created using the same image description, unless you use group IDs.

The matrix, layer, visible, and graphics mode sprite properties have the same meaning for a sprite in a sprite track as for a sprite created in a sprite world.

As with sprite worlds, you can create a sprite track that has a solid background color, a background image composed of the images of one or more background sprites, or both a background color and a background image.

Key Frame Samples and Override Samples

As discussed, a sprite track is defined by one or more key frame samples, each followed by any number of override samples. A key frame sample for a sprite track defines the following aspects of a sprite track:

  • The number of sprites in the scene and their initial properties.

  • All of the shared image data to be used by the sprites in the scene, including image data to be used in the subsequent override samples. Because a key frame sample contains the image data for the scene, the key frame sample tends to be larger than its subsequent override samples.

An override sample overrides some aspect of the key frame sample. For example, an override sample might modify the location of sprites defined in the key frame sample. Override samples do not contain any image data, so they can be very small. An override sample can show or hide a sprite defined in the key frame sample, but it cannot define new sprites or remove sprites defined in its key frame sample. An override sample can override any number of properties for any number of sprites. For example, a single override sample might change the layer and location of sprite ID 3, and hide sprite ID 10.

There are two sprite track formats that define how a key frame sample and its subsequent override samples are interpreted. If the current sample is a key frame sample, the key frame sample alone fully describes the current state of the track. If the current sample is an override sample, the current state may differ depending on the sprite track format:

  • If the sprite track format is kKeyFrameAndSingleOverride, the current state is defined by the most recent key frame sample and the current override sample. This is the default format. The advantage of this format is that it allows for excellent performance during random access. A sprite track that uses this format can play backwards and drop frames smoothly. The disadvantage of this format is that the file size of the track may be larger than a track that uses the other format.

  • If the sprite track format is kKeyFrameAndAllOverrides, the current state is defined by the most recent key sample and all subsequent override samples, including the current override sample. This format results in a smaller file size. However, you should not use this format if you want your sprite track to play backwards or drop frames smoothly. When you play a movie that contains a sprite track whose format is kKeyFrameAndAllOverrides, you should configure the movie to play all frames.

Sprite Track Media Format

The sprite track media format is hierarchical and based on QT atoms and atom containers. A sprite track is defined by one or more key frame samples, each followed by any number of override samples. A key frame sample and its subsequent override samples define a scene in the sprite track.

A key frame sample is a QT atom container that contains atoms defining the sprites in the scene and their initial properties. The override samples are other QT atom containers that contain atoms that modify sprite properties, thereby animating the sprites in the scene. In addition to defining properties for individual sprites, you can also define properties that apply to an entire sprite track. For more information about QT atoms and atom containers, see Chapter 8, QuickTime Atoms and Atom Containers, and the book QuickTime File Format (see bibliography).

Figure 3-1 shows the high-level structure of a sprite track key frame sample. Each atom in the atom container is represented by its atom type, atom ID, and, if it is a leaf atom, the type of its data.

Figure 3-1  A key frame sample atom container
A key frame sample atom container

The QT atom container contains one child atom for each sprite in the key frame sample. Each sprite atom has a type of kSpriteAtomType. The sprite IDs are numbered from 1 to the number of sprites defined by the key frame sample (numSprites).

Each sprite atom contains leaf atoms that define the properties of the sprite, as shown in Figure 3-2. For example, the kSpritePropertyLayer property defines a sprite’s layer. Each sprite property atom has an atom type that corresponds to the property and an ID of 1.

Figure 3-2  Atoms that describe a sprite and its properties
Atoms that describe a sprite and its properties

In addition to the sprite atoms, the QT atom container contains one atom of type kSpriteSharedDataAtomType with an ID of 1. The atoms contained by the shared data atom describe data that is shared by all sprites. The shared data atom contains one atom of type kSpriteImagesContainerAtomType with an ID of 1 (Figure 3-3).

The image container atom contains one atom of type kImageAtomType for each image in the key frame sample. The image atom IDs are numbered from 1 to the number of images (numImages). Each image atom contains a leaf atom that holds the image data (type kSpriteImageDataAtomType) and an optional leaf atom (type kSpriteNameAtomType) that holds the name of the image.

Figure 3-3  Atoms that describe sprite images
Atoms that describe sprite images

Assigning Group IDs

In earlier versions of QuickTime, sprites could only display images with the same image description. This restriction has been relaxed, but you must assign group IDs to sets of equivalent images in your key frame sample. For example, if the sample contains 10 images where the first 2 images are equivalent, and the last 8 images are equivalent, you could assign a group ID of 1000 to the first 2 images, and a group ID of 1001 to the last 8 images. This divides the images in the sample into two sets. The actual ID does not matter; it just needs to be a unique positive integer.

Each image in a sprite media key frame sample is assigned to a group. You add an atom of type kSpriteImageGroupIDAtomType as a child of the kSpriteImageAtomType atom and set its leaf data to a long containing the group ID.

You must assign group IDs to your sprite sample if you want a sprite to display images with non-equivalent image descriptions (i.e., images with different dimensions).

Sprite Image Registration

Sprite images have a default registration point of 0, 0. To specify a different point, you add an atom of type kSpriteImageRegistrationAtomType as a child atom of the kSpriteImageAtomType and set its leaf data to a FixedPoint value with the desired registration point.

The format of an override sample is identical to that of a key frame sample with the following exceptions:

  • An override sample does not contain images, which means it does not contain an atom of type kSpriteImagesContainerAtomType or any of its children.

  • In an override sample, all of the sprite atoms and sprite property atoms are optional.

For example, to define an override sample that modifies the location of the third sprite defined by the previous key frame sample, you would create a QT atom container and add the following atoms to it (assuming that the sprite track format is of type kKeyFrameAndSingleOverride):

Figure 3-4  An example of an override sample atom container
An example of an override sample atom container

Sprite Track Properties

In addition to defining properties for individual sprites, you can also define properties that apply to an entire sprite track. These properties may override default behavior or provide hints to the sprite media handler. The following sprite track properties are supported:

  • The kSpriteTrackPropertyBackgroundColor property specifies a background color for the sprite track. The background color is used for any area that is not covered by regular sprites or background sprites. If you do not specify a background color, the sprite track uses black as the default background color.

  • The kSpriteTrackPropertyOffscreenBitDepth property specifies a preferred bit depth for the sprite track’s offscreen buffer. The allowable values are 8 and 16. To save memory, you should set the value of this property to the minimum depth needed. If you do not specify a bit depth, the sprite track allocates an offscreen buffer with the depth of the deepest intersecting monitor.

  • The kSpriteTrackPropertySampleFormat property specifies the sample format for the sprite track. If you do not specify a sample format, the sprite track uses the default format, kKeyFrameAndSingleOverride.

To specify sprite track properties, you create a single QT atom container and add a leaf atom for each property you want to specify. To add the properties to a sprite track, you call the media handler function SetMediaPropertyAtom. To retrieve a sprite track’s properties, you call the media handler function GetMediaPropertyAtom.

The sprite track properties and their corresponding data types are listed in Table 3-1.

Table 3-1  Sprite track properties

Atom type

Atom ID

Leaf data type

kSpriteTrackPropertyBackgroundColor

1

RGBColor

kSpriteTrackPropertyOffscreenBitDepth

1

unsigned short

kSpriteTrackPropertySampleFormat

1

long

kSpriteTrackPropertyHasActions

1

Boolean

kSpriteTrackPropertyQTIdleEventsFrequency

1

UInt32

kSpriteTrackPropertyVisible

1

Boolean

kSpriteTrackPropertyScaleSpritesToScaleWorld

1

Boolean

Alternate Sources for Sprite Image Data

A sprite in a sprite track can obtain its image data from sources other than the images in the sprite track’s key frame sample. The alternate image data overrides a particular image index in the sprite track so that all sprites with that image index will use the image data provided by the alternate source.

A sprite track can receive image data from another track within the same movie, called a modifier track. This is useful for compositing traditional video tracks with sprites. For example, you might create a sprite track in which sprite characters are watching television. The sprite track can receive video from another track, called a modifier track, to use as the image data for the television screen sprite. Other sprites can move in front of and behind the television. A sprite track can have more than one modifier track feeding it image data and more than one sprite can use the image data from a modifier track at one time.

In order for a sprite to receive image data from a modifier track, you must call the AddTrackReference function to link the modifier track to the sprite track that it modifies. In addition, you must update the sprite media’s input map with an atom that specifies the input type (kTrackModifierTypeImage) and an atom that specifies the index of the image to replace (kSpritePropertyImageIndex).

A sprite track can also receive sprite image data from an application. For example, an application might provide live, digitized video data to a sprite track by calling MediaSetNonPrimarySourceData.

Supported Modifier Inputs

In addition to receiving image data, a sprite track can receive modifier track data to control its sprites. The following modifier inputs are supported:

  • images from a video track (kTrackModifierTypeImage)

  • a matrix from a base track (kTrackModifierObjectMatrix)

  • a graphics mode from a base track (kTrackModifierObjectGraphicsMode)

  • an image index from a base track (kTrackModifierObjectImageIndex)

  • an object layer from a base track (kTrackModifierObjectLayer)

  • an object visible from a base track (kTrackModifierObjectVisible)

For example, a modifier track can send matrices to individual sprites to control their locations. To do this, you set up a modifier track, such as a tween track, to send matrix data to the sprite track. You must update the sprite media’s input map with an atom that specifies the input type (kTrackModifierObjectMatrix) and an atom that specifies the ID of the sprite to replace (kTrackModifierObjectID). If the sprite track also contains matrices to move the sprites, the results are undefined.

Hit-Testing Flags

The following hit-testing flags are for use with SpriteMediaHitTestAllSprites and SpriteMediaHitTestOneSprite:

  • spriteHitTestInvisibleSprites, which you set if you want invisible sprites to be hit-tested along with visible ones.

  • spriteHitTestLocInDisplayCoordinates, which you set if the hit-testing point is in display coordinates instead of local sprite track coordinates.

  • spriteHitTestIsClick, which you set if you want the hit-testing operation to pass a click on to the codec currently rendering the sprites image. For example, this can be used to make the Ripple codec ripple.

Using Sprites in a Sprite Track

This section describes some of the ways in which your application can use sprites in a sprite track in QuickTime.

Referenced Sprite Images

In QuickTime, the sprite track can use images which are external to its movie’s media. The images may be located somewhere on the Internet, in a local file, or anywhere else that a QuickTime Data Handler can read them from.

Since images located on a network server may take some time to load (or possibly never show up), referenced images are loaded asynchronously. Sprites not using referenced images will be created and will be active while the referenced images are loading. You may optionally supply a proxy image that will be displayed until the referenced image has been loaded. If you don’t supply a proxy image, sprites using the referenced image will be disabled (invisible and not responsive to mouse events) until it is loaded.

Otherwise, referenced images are the same as traditional ones. They are defined in a sprite key frame sample, available until the next key frame sample is loaded, used by a sprite setting its imageIndex property to the images index, and may be shared by multiple sprites.

Specifying Sprite Button Behaviors

In QuickTime, sprites in a sprite track may specify some simple button behaviors. These behaviors may control the sprite’s image, the system cursor, and the status message displayed in a Web browser. These behaviors are a compact shortcut for a very common set of actions which result in more efficient movies.

Button behaviors may also be added to a sprite. These behaviors are intended to make the common task of creating buttons in a sprite track easy—you basically just fill in a template. Three types of behaviors are available; you choose one or more of them. The behaviors each change a type of property associated with a button and are triggered by the mouse states notOverNotPressed, overNotPressed, overPressed, and notOverPressed. The three properties changed are

  • the sprites’ imageIndex

  • the ID of a cursor to be displayed

  • the ID of a status string variable displayed in the URL status area of a Web browser.

Setting a property’s value to -1 means don’t change it.

The sprite track handles letting one sprite act as an active button at a time. The behaviors are prepended to the sprite’s list of actions, so they may be overriden by actions if desired. To use the behaviors, you fill in the new atoms as follows, using the description key:

kSpriteAtomType
    <kSpriteBehaviorsAtomType>, 1
        <kSpriteImageBehaviorAtomType>
            [QTSpriteButtonBehaviorStruct]
        <kSpriteCursorBehaviorAtomType>
            [QTSpriteButtonBehaviorStruct]
        <kSpriteStatusStringsBehaviorAtomType>
            [QTSpriteButtonBehaviorStruct]

Using the Action Handler Sprite Property

QuickTime includes an action handler property: kSpritePropertyActionHandlingSpriteID whose data type is QTAtomID. You set this sprite property to the ID of another sprite in the sprite track that you wish to delegate QT event handling to.

String Variable Support

In QuickTime, sprite track variables may contain either strings or floating point numbers. These variables may be used for all action and operand parameters that accept strings, such as GotoURL. Additionally, they may be concatenated together to create new string variables.

The kActionSpriteTrackSetVariableToString action has been introduced to allow you to set a variable to a string value. You still use the kActionSpriteTrackSetVariable action to set a variable to a floating-point number.

When a sprite track variable is retrieved and used via the kOperandSpriteTrackVariable operand, it will be coerced to the required type. If it is used as part of an expression and it is a string, it will be converted to a floating-point number. If used as a string parameter, it will be converted to a C or Pascal string as needed from a floating-point number or string variable.

When a floating point number which does not contain an integer value is coerced to a string, a format of up to five digits before the decimal point and three digits afterwards is used.

Creating a QuickTime Sprite Movie

The sample code in this section illustrates how you can create a sprite movie containing one sprite track. The sprite track contains a static background picture sprite (or just a colored background, depending on the value of the global variable gUseBackgroundPicture) and three other sprites that change their properties over time.

The track’s media contains only one key frame sample followed by many override samples. The key frame contains all of the images used by the sprites; the override frames only contain the overrides of the locations, image indices, and layers needed for the other sprites.

This sample code also shows how to test for mouse clicks (“hits”) on a sprite. It uses the function SpriteMediaHitTestAllSprites to find mouse clicks on the sprites in the first sprite track in a movie. If the user clicks on a sprite, we toggle the visibility state of the sprite.

Listing 3-1  QTSprites sample code that lets you create a sprite movie with a single sprite track

// header files
#include “QTSprites.h”
 
// global variables
Boolean         gUseBackgroundPicture = true;
                                // do we display a background picture?
 
 
ApplicationDataHdl QTSprites_InitWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
    Track                   myTrack = NULL;
    MediaHandler            myHandler = NULL;
 
    myAppData = (ApplicationDataHdl)NewHandleClear(sizeof(ApplicationDataRecord));
    if (myAppData != NULL) {
 
        myTrack = GetMovieIndTrackType((**theWindowObject).fMovie, 1,  SpriteMediaType, movieTrackMediaType | movieTrackEnabledOnly);
        if (myTrack != NULL)
            myHandler = GetMediaHandler(GetTrackMedia(myTrack));
 
        // remember the sprite media handler
        (**myAppData).fMovieHasSprites = (myTrack != NULL);
        (**myAppData).fSpriteHandler = myHandler;
    }
 
    return(myAppData);
}

You call QTSprites_DumpWindowData to get rid of any window-specific data for the sprite media handler.

void QTSprites_DumpWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
 
    myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL)
        DisposeHandle((Handle)myAppData);
}

To create a QuickTime movie containing a sprite track, you call QTSprites_CreateSpritesMovie.

OSErr QTSprites_CreateSpritesMovie (void)
{
    short                   myResRefNum = 0;
    short                   myResID = movieInDataForkResID;
    Movie                   myMovie = NULL;
    Track                   myTrack;
    Media                   myMedia;
    StandardFileReply       myReply;
    QTAtomContainer         mySample = NULL;
    QTAtomContainer         mySpriteData = NULL;
    RGBColor                myKeyColor;
    Point                   myLocation, myIconLocation;
    short                   isVisible, myLayer, myIndex, i, myDelta,
                            myIconMinH, myIconMaxH;
    long                    myFlags = createMovieFileDeleteCurFile |
                            createMovieFileDontCreateResFile;
    OSErr                   myErr = noErr;

You create a new movie file and ask the user for the name of the new movie file.

    StandardPutFile(“\pSprite movie file name:”, “\pSprite.mov”,
                    &myReply);
    if (!myReply.sfGood)
        goto bail;

You create a movie file for the destination movie.

    myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE(‘TVOD’),
                            smSystemScript, myFlags, &myResRefNum,
                            &myMovie);
    if (myErr != noErr)
        goto bail;

You create the sprite track and media.

        myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16), ((long)kSpriteTrackHeight << 16), kNoVolume);
    myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);

Now you create a key frame sample containing the sprites and all of their shared images and create a new, empty key frame sample.

    myErr = QTNewAtomContainer(&mySample);
    if (myErr != noErr)
        goto bail;
    myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff;// white

Now you add images to the key frame sample.

    AddPICTImageToKeyFrameSample(mySample, kIconPictID, &myKeyColor,
                                kIconImageIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kWorldPictID, &myKeyColor,
                                kWorldImageIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kBackgroundPictID,
                                &myKeyColor, kBackgroundImageIndex, NULL,
                                NULL);
    for (myIndex = 1; myIndex <= kNumSpaceShipImages; myIndex++)
        AddPICTImageToKeyFrameSample(mySample, kFirstSpaceShipPictID +
        myIndex - 1, &myKeyColor, myIndex + 3, NULL, NULL);

You add samples to the sprite track’s media.

    BeginMediaEdits(myMedia);
 
    myErr = QTNewAtomContainer(&mySpriteData);
    if (myErr != noErr)
        goto bail;
 
    // the background image
    if (gUseBackgroundPicture) {
        myLocation.h    = 0;
        myLocation.v    = 0;
        isVisible       = true;
        myLayer         = kBackgroundSpriteLayerNum;    // this makes the
                                            // sprite a background sprite
        myIndex         = kBackgroundImageIndex;
        myErr = SetSpriteData(mySpriteData, &myLocation, &isVisible,
                                &myLayer, &myIndex, NULL, NULL, NULL);
        if (myErr != noErr)
            goto bail;
        AddSpriteToSample(mySample, mySpriteData,
                            kBackgroundSpriteAtomID);
    }
 
    // the space ship sprite
    myLocation.h    = 0;
    myLocation.v    = 60;
    isVisible       = true;
    myLayer         = -1;
    myIndex         = kFirstSpaceShipImageIndex;
    myErr = SetSpriteData(mySpriteData, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, NULL);
    if (myErr != noErr)
        goto bail;
    AddSpriteToSample(mySample, mySpriteData, kSpaceShipSpriteAtomID);
 
    // the world sprite
    myLocation.h    = (kSpriteTrackWidth / 2) - 24;
    myLocation.v    = (kSpriteTrackHeight / 2) - 24;
    isVisible       = true;
    myLayer         = 1;
    myIndex         = kWorldImageIndex;
    myErr = SetSpriteData(mySpriteData, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, NULL);
    if (myErr != noErr)
        goto bail;
    AddSpriteToSample(mySample, mySpriteData, kWorldSpriteAtomID);
 
    // the icon sprite
    myIconMinH          = (kSpriteTrackWidth / 2) - 116;
    myIconMaxH          = myIconMinH + 200;
    myDelta             = 2;
    myIconLocation.h    = myIconMinH;
    myIconLocation.v    = (kSpriteTrackHeight / 2) - (24 + 12);
    isVisible           = true;
    myLayer             = 0;
    myIndex             = kIconImageIndex;
    myErr = SetSpriteData(mySpriteData, &myIconLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, NULL);
    if (myErr != noErr)
        goto bail;
    AddSpriteToSample(mySample, mySpriteData, kIconSpriteAtomID);
 
    // add the key frame sample to the sprite track media

To add the sample data in a compressed form, you would use a QuickTime data codec to perform the compression. You replace the call to the utility AddSpriteSampleToMedia with a call to the utility AddCompressedSpriteSampleToMedia to accomplish this.

    AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration,
                            true, NULL);
    //AddCompressedSpriteSampleToMedia(myMedia, mySample,
                                        kSpriteMediaFrameDuration, true,
                                        zlibDataCompressorSubType, NULL);

Now you add a few override samples to move the space ship and icon, and to change the icon’s layer.

    // original space ship location
    myIndex         = kFirstSpaceShipImageIndex;
    myLocation.h    = 0;
    myLocation.v    = 80;
    isVisible       = true;
 
    for (i = 1; i <= kNumOverrideSamples; i++) {
        QTRemoveChildren(mySample, kParentAtomIsContainer);
        QTRemoveChildren(mySpriteData, kParentAtomIsContainer);
 
        // every third frame, bump the space ship’s image index (so that
        // it spins as it moves)
        if ((i % 3) == 0) {
            myIndex++;
            if (myIndex > kLastSpaceShipImageIndex)
                myIndex = kFirstSpaceShipImageIndex;
        }
 
        // every frame, bump the space ship’s location (so that it moves
        // as it spins)
        myLocation.h += 2;
        myLocation.v += 1;
 
        if (isVisible)
            SetSpriteData(mySpriteData, &myLocation, NULL, NULL,
            &myIndex, NULL, NULL, NULL);
        else {
            isVisible = true;
            SetSpriteData(mySpriteData, &myLocation, &isVisible, NULL,
            &myIndex, NULL, NULL, NULL);
        }
 
        AddSpriteToSample(mySample, mySpriteData, 2);
 
        // make the icon move and change layer
        QTRemoveChildren(mySpriteData, kParentAtomIsContainer);
        myIconLocation.h += myDelta;
 
        if (myIconLocation.h >= myIconMaxH ) {
            myIconLocation.h = myIconMaxH;
            myDelta = -myDelta;
        }
 
        if (myIconLocation.h <= myIconMinH ) {
            myIconLocation.h = myIconMinH;
            myDelta = -myDelta;
        }
 
        if (myDelta > 0)
            myLayer = 0;
        else
            myLayer = 3;
 
        SetSpriteData(mySpriteData, &myIconLocation, NULL, &myLayer,
                        NULL, NULL, NULL, NULL);
        AddSpriteToSample(mySample, mySpriteData, 4);
 
        AddSpriteSampleToMedia(myMedia, mySample,
                                kSpriteMediaFrameDuration, false, NULL);
    }
 
    EndMediaEdits(myMedia);
 
    // add the media to the track
    InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia),
                            fixed1);

Now you set the sprite track properties.

    if (!gUseBackgroundPicture) {
        QTAtomContainer     myTrackProperties;
        RGBColor            myBackgroundColor;
 
        // add a background color to the sprite track
        myBackgroundColor.red = EndianU16_NtoB(0x8000);
        myBackgroundColor.green = EndianU16_NtoB(0);
        myBackgroundColor.blue = EndianU16_NtoB(0xffff);
 
        QTNewAtomContainer(&myTrackProperties);
        QTInsertChild(myTrackProperties, 0,
                        kSpriteTrackPropertyBackgroundColor, 1, 1,
                        sizeof(RGBColor), &myBackgroundColor, NULL);
 
        myErr = SetMediaPropertyAtom(myMedia, myTrackProperties);
        if (myErr != noErr)
            goto bail;
 
        QTDisposeAtomContainer(myTrackProperties);
    }

Finally, you finish up and add the movie resource to the movie file.

    myErr = AddMovieResource(myMovie, myResRefNum, &myResID,
                            myReply.sfFile.name);
 
bail:
    if (mySample != NULL)
        QTDisposeAtomContainer(mySample);
 
    if (mySpriteData != NULL)
        QTDisposeAtomContainer(mySpriteData);
 
    if (myResRefNum != 0)
        CloseMovieFile(myResRefNum);
 
    if (myMovie != NULL)
        DisposeMovie(myMovie);
 
    return(myErr);
}

You call QTSprites_HitTestSprites to determine whether a mouse click is on a sprite; return true if it is, false otherwise. This routine is intended to be called from your movie controller action filter function, in response to mcActionMouseDown actions.

Boolean QTSprites_HitTestSprites (WindowObject theWindowObject,
                                    EventRecord *theEvent)
{
    ApplicationDataHdl      myAppData = NULL;
    MediaHandler            myHandler = NULL;
    Boolean                 isHandled = false;
    long                    myFlags = 0L;
    QTAtomID                myAtomID = 0;
    Point                   myPoint;
    ComponentResult         myErr = noErr;
 
    myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        goto bail;
 
    if (theEvent == NULL)
        goto bail;
 
    myHandler = (**myAppData).fSpriteHandler;
    myFlags = spriteHitTestImage | spriteHitTestLocInDisplayCoordinates | spriteHitTestInvisibleSprites;
    myPoint = theEvent->where;
 
    myErr = SpriteMediaHitTestAllSprites(myHandler, myFlags, myPoint,
                                            &myAtomID);
    if ((myErr == noErr) && (myAtomID != 0)) {
        Boolean             isVisible;
 
        // the user has clicked on a sprite;
        // for now, we’ll just toggle the visibility state of the sprite
        SpriteMediaGetSpriteProperty(myHandler, myAtomID, kSpritePropertyVisible, (void *)&isVisible);
        SpriteMediaSetSpriteProperty(myHandler, myAtomID, kSpritePropertyVisible, (void *)!isVisible);
 
        isHandled = true;
    }
 
bail:
    return(isHandled);

Useful Sprite Media Handler Functions

This section describes some of the sprite media handler functions that are useful and available to your application. For a complete listing of all functions related to sprites and the sprite media handler, refer to the QuickTime API Reference, which is available at

http://developer.apple.com/documentation/Quicktime/QuickTime.html

SpriteMediaSetSpriteProperty

You use the SpriteMediaSetSpriteProperty function to set the specified property of a sprite.

pascal ComponentResult SpriteMediaSetSpriteProperty (MediaHandler mh,
                                                    QTAtomID spriteID,
                                                    long propertyType,
                                                    void* propertyValue);

You call this function to modify a property of a sprite. You set the propertyType parameter to the property you want to modify. Set the spriteID parameter to the ID of the sprite whose property you want to set.

The type of data you pass for the propertyValue parameter depends on the property type. Table 3-2 lists the sprite properties and the data types of the corresponding property values.

Table 3-2  Sprite properties and data types

Sprite Property

Data Type

kSpritePropertyMatrix

MatrixRecord *

kSpritePropertyVisible

short

kSpritePropertyLayer

short

kSpritePropertyGraphicsMode

ModifierTrackGraphicsModeRecord *

kSpritePropertyImageIndex

short

SpriteMediaGetSpriteProperty

You use the SpriteMediaGetSpriteProperty function to retrieve the value of the specified sprite property.

pascal ComponentResult SpriteMediaGetSpriteProperty (MediaHandler mh,
                                                    QTAtomID spriteID,
                                                    long propertyType,
                                                    void* propertyValue);

You call this function to retrieve a value of a sprite property. You set the propertyType parameter to the property you want to retrieve. You set the spriteID parameter to the ID of the sprite whose property you want to retrieve.

On return, the propertyValue parameter contains a pointer to the specified property’s value; the data type of that value depends on the property. The following table lists the sprite properties and the data types of the corresponding property values.

Sprite Property

Data Type

kSpritePropertyMatrix

MatrixRecord *

kSpritePropertyVisible

short *

kSpritePropertyLayer

short *

kSpritePropertyGraphicsMode

ModifierTrackGraphicsModeRecord *

kSpritePropertyImageIndex

short *

SpriteMediaHitTestAllSprites

You use the SpriteMediaHitTestAllSprites function to determine whether any sprites are at a specified location.

pascal ComponentResult SpriteMediaHitTestAllSprites (MediaHandler mh,
                                                    long flags,
                                                    Point loc,
                                                QTAtomID *spriteHitID);

You call this function to determine whether any sprites exist at a specified location in the coordinate system of a sprite track’s movie. You can pass flags to this function to control the hit testing operation more precisely. For example, you may want the hit test operation to detect a sprite whose bounding box contains the specified location.

SpriteMediaCountSprites

You use the SpriteMediaCountSprites function to retrieve the number of sprites that currently exist in a sprite track.

pascal ComponentResult SpriteMediaCountSprites (MediaHandler mh,
                                                short* numSprites);

This function determines the number of sprites that currently exist based on the key frame that is in effect.

SpriteMediaCountImages

You use the SpriteMediaCountImages function retrieves the number of images that currently exist in a sprite track.

pascal ComponentResult SpriteMediaCountImages (MediaHandler mh,
                                                short* numImages);

This function determines the number of images that currently exist based on the key frame that is in effect.

SpriteMediaGetIndImageDescription

You use the SpriteMediaGetIndImageDescription function retrieves an image description for the specified image in a sprite track.

pascal ComponentResult SpriteMediaGetIndImageDescription (MediaHandler mh,
                                                        short imageIndex,
                                ImageDescriptionHandle imageDescription);

You set the imageIndex parameter to the index of the image whose image description you want to retrieve. The index must be between one and the number of available images. You can determine how many images are available by calling SpriteMediaCountImages.

The handle specified by the imageDescription parameter must be unlocked; this function resizes the handle if necessary.

SpriteMediaGetDisplayedSampleNumber

You use the SpriteMediaGetDisplayedSampleNumber function to retrieve the number of the sample that is currently being displayed.

pascal ComponentResult SpriteMediaGetDisplayedSampleNumber (MediaHandler mh,
                                                        long* sampleNum);

You call this function when you need to retrieve the sample number of the sample that is being displayed.

SpriteMediaGetSpriteName

You use the SpriteMediaGetSpriteName function to return the name of the sprite with the specified ID from the currently displayed sample.

pascal ComponentResult SpriteMediaGetSpriteName(MediaHandler mh,
                                                QTAtomID spriteID,
                                                Str255  spriteName);

SpriteMediaGetImageName

You use the SpriteMediaGetImageName function to return the name of the image with the specified index from the current key frame sample.

pascal ComponentResult SpriteMediaGetImageName(MediaHandler mh,
                                                short imageIndex,
                                                Str255 imageName);

SpriteMediaHitTestOneSprite

You use the SpriteMediaHitTestOneSprite function to perform a hit testing operation on the sprite specified by the spriteID.

pascal ComponentResult SpriteMediaHitTestOneSprite(MediaHandler mh,
                                                    QTAtomID spriteID,
                                                    long flags,
                                                    Point loc,
                                                    Boolean * wasHit);

SpriteMediaSpriteIndexToID

You use the SpriteMediaSpriteIndexToID function to return the ID of the sprite specified by spriteIndex in spriteID.

pascal ComponentResult SpriteMediaSpriteIndexToID( MediaHandler mh,
                                                short spriteIndex,
                                                QTAtomID *  spriteID);

SpriteMediaSpriteIDToIndex

You use the SpriteMediaSpriteIDToIndex function to return the sprite index of the sprite specified by the spriteID.

pascal ComponentResult SpriteMediaSpriteIDToIndex(MediaHandler mh,
                                                QTAtomID spriteID,
                                                short *  spriteIndex);

SpriteMediaGetIndImageProperty

You use the SpriteMediaGetIndImageProperty function to return a property value for the image specified by imageIndex.

pascal ComponentResult SpriteMediaGetIndImageProperty(MediaHandler mh,
                                                    short imageIndex,
                                                 long imagePropertyType,
                                             void * imagePropertyValue);

Sprites Functions Specific to Wired Sprites

The following routines are specific to sprite tracks using wired sprites.

SpriteMediaSetActionVariable

You use the SpriteMediaSetActionVariable function to set the value of the sprite track variable with the ID of the variable to the supplied value.

pascal ComponentResult SpriteMediaSetActionVariable (MediaHandler  mh,
                                                    QTAtomID variableID,
                                                    const float * value);

SpriteMediaGetActionVariable

You use the SpriteMediaGetActionVariable to return the value of the sprite track variable with the specified ID.

pascal ComponentResult SpriteMediaGetActionVariable(MediaHandler mh,
                                                    QTAtomID variableID,
                                                    float * value);

SpriteDescription Structure

Sprite samples may be compressed using a data compression codec.

struct SpriteDescription {
    long        descSize;       /* total size of
                            SpriteDescription including extra data */
    long        dataFormat;     /*  */
    long        resvd1;         /* reserved for apple use */
    short       resvd2;
    short       dataRefIndex;
    long        version;            /* which version is this data */
    OSType      decompressorType;   /* which decompressor to use, 0 for
                                    no decompression */
    long        sampleFlags;        /* how to interpret samples */
};
typedef struct SpriteDescription        SpriteDescription;
typedef SpriteDescription *             SpriteDescriptionPtr;
typedef SpriteDescriptionPtr *          SpriteDescriptionHandle;
decompressorType

This field in the SpriteDescription sample description structure allows a data decompressor component type to be specified. If this field is nonzero, a component of the specified type is used to decompress the sprite sample when it is loaded.

Wired Actions in QuickTime

QuickTime 5.01 introduces a number of new wired actions and operands, which are described in this section.

New Wired Actions

The following new wired actions have been added in QuickTime 5:

kActionListSetFromURL = 13317   /* (C string url, C string
                                targetParentPath ) */

This allows the scripter to use an XML file to initialize a list.

kActionSendAppMessage = 6160    /* (long appMessageID) */

This allows you to send a wired movie message to a host application like QuickTime Player.

The application message can be one of the following defines:

kQTAppMessageSoftwareChanged = 1        /* notification to app that
                                        installed QuickTime
                                        software has been updated*/
kQTAppMessageWindowCloseRequested = 3   /* request for app to close
                                        window containing
                                        movie controller*/
kQTAppMessageExitFullScreenRequested = 4/* request for app to turn off
                                        full screen mode if active*/
kActionFlashTrackSetFlashVariable = 10245 /* (C string path, C string
                        name, C string value, Boolean updateFocus) */

Sets a Flash variable to the value.

kActionFlashTrackDoButtonActions = 10246 /* (C string path, long
                                        buttonID, long transition) */

Sends a message to a Flash button to perform a transition. This causes whatever Flash or QuickTime action associated with the button to perform.

kActionTextTrackPasteText = 12288/* (C string theText, long startSelection, long
                                        endSelection ) */

Replaces a selection in the text track with theText.

kActionTextTrackSetTextBox= 12291 /* (short left, short top, short right,
                                    short bottom) */

Changes the textBox of text track to the passed in size.

kActionTextTrackSetTextStyle = 12292 /* (Handle textStyle) */

Changes text track style - a TextStyle record

kActionTextTrackSetSelection = 12293 /* (long startSelection, long endSelection ) */

Sets the text track selection.

kActionTextTrackSetBackgroundColor = 12294/* (ModifierTrackGraphicsModeRecord
                                                backgroundColor ) */

Sets the text track background color.

kActionTextTrackSetForegroundColor = 12295/* (ModifierTrackGraphicsModeRecord
                                            foregroundColor ) */

Sets the text color.

kActionTextTrackSetFace = 12296 /* (long fontFace ) */

Sets the text face (style) of all text.

kActionTextTrackSetFont = 12297 /* (long fontID ) */

Sets the text font of all text.

kActionTextTrackSetSize = 12298 /* (long fontSize ) */

Sets the text size of all text.

kActionTextTrackSetAlignment = 12299/* (short alignment ) */

Sets the text alignment as in:

    teJustLeft                  = 0
    teJustCenter                = 1
    teJustRight                 = -1
    teForceLeft                 = -2 /* new names for the
                                 Justification (word alignment) styles */
    teFlushDefault              = 0  /*flush according to the line
                                 direction */
    teCenter                    = 1 /*center justify (word alignment) */
    teFlushRight                = -1 /*flush right for all scripts */
kActionTextTrackSetHilite = 12300 /* (long startHighlight, long
endHighlight,
ModifierTrackGraphicsModeRecord highlightColor ) */

Highlights from the startHighlight offset to the endHighlight offset.

kActionTextTrackSetDropShadow = 12301 /* (Point dropShadow, short
transparency ) */

Sets the drop shadow parameters. This only works if displayFlags has been set with dfDropShadow.

kActionTextTrackSetDisplayFlags = 12302 /* (long flags ) */

Sets the text display flags as in:

    dfDontDisplay               = 1 << 0    /* Don't display the text*/
    dfDontAutoScale             = 1 << 1    /* Don't scale text as track                                             bounds grows or shrinks*/
    dfClipToTextBox             = 1 << 2    /* Clip update to the textbox*/
    dfUseMovieBGColor           = 1 << 3    /* Set text background to                                             movie's background color*/
    dfShrinkTextBoxToFit        = 1 << 4    /* Compute minimum box to fit
                                            the sample*/
    dfScrollIn                  = 1 << 5    /* Scroll text in until
                                            last of text is in view */
    dfScrollOut                 = 1 << 6    /* Scroll text out until last
                                            of text is gone (if both set,
                                            scroll in then out)*/
    dfHorizScroll               = 1 << 7    /* Scroll text horizontally
                                             (otherwise it's vertical)*/
    dfReverseScroll             = 1 << 8    /* vert: scroll down rather
                                            than up; horiz: scroll
                                            backwards
                                            (justfication  dependent)*/
    dfContinuousScroll          = 1 << 9    * new samples cause previous
                                            samples to scroll out */
    dfFlowHoriz                 = 1 << 10   /* horiz scroll text flows in
                                            textbox rather than extend to
                                            right */
    dfContinuousKaraoke         = 1 << 11   /* ignore begin offset, hilite
                                            everything up to the end
                                            offset(karaoke)*/
    dfDropShadow                = 1 << 12   /* display text with a drop
                                            shadow */
    dfAntiAlias                 = 1 << 13   /* attempt to display text
                                            anti aliased*/
    dfKeyedText                 = 1 << 14   /* key the text over
                                            background*/
    dfInverseHilite             = 1 << 15   /* Use inverse hiliting rather
                                            than using
    dfTextColorHilite           = 1 << 16   /* changes text color in place
                                            of hiliting. */
kActionTextTrackSetScroll= 12303,/* (long delay ) */

Sets the time delay for start of the scroll. This only works when scroll flags are set in displayFlags.

kActionTextTrackRelativeScroll = 12304 /* (short deltaX, short deltaY ) */

Scrolls the text in the text box by the delta amounts.

kActionTextTrackFindText = 12305 /* (long flags, Str255 theText,
                                    ModifierTrackGraphicsModeRecord
                                    highlightColor ) */

Finds text in the track. Similar in operation to TextMediaFindNextText since this is what it uses.

kActionTextTrackSetHyperTextFace = 12306/* (short index, long fontFace ) */

Sets the text face (style) of the indexed hypertext.

kActionTextTrackSetHyperTextColor = 12307 /* (short index,
                                    ModifierTrackGraphicsModeRecord
                                    highlightColor ) */

Sets the text color of the indexed hypertext.

kActionTextTrackKeyEntry = 12308 /* (short character ) */

Replaces the selection with the character.

kActionTextTrackMouseDown = 12309 /* no params */

Passes the mouse click to the text track, which allows for selecting text or an insertion point when kKeyEntryScript is turned on.

kActionTextTrackSetEditable = 12310 /* (short editState) */

Controls the key entry state of the text track:

    #define kKeyEntryDisabled       0
    #define kKeyEntryDirect         1
    #define kKeyEntryScript         2

kKeyEntryDisabled is default.

If kKeyEntryDirect is on, then key events are passed directly to the text track.

If kKeyEntryScript is on, then scripted mouse and key events are allowed.

kActionListAddElement = 13312 /* (C string parentPath, short atIndex, C
                            string newElementName) */

Adds the element to the target list.

kActionListRemoveElements = 13313 /* (C string parentPath, short startIndex,  short endIndex) */

Removes the element from the target list.

kActionListSetElementValue = 13314 /* (C string elementPath, C string
                                    valueString) */

Sets the list element value.

kActionListPasteFromXML = 13315 /* (C string xml, C string targetParentPath,
                                    short startIndex) */

Pastes an XML-formatted list into the target list at startIndex.

kActionListSetMatchingFromXML = 13316 /* (C string xml, C string
 targetParentPath) */

Replaces the matching element values in the target list.

kActionListSetMatchingFromXML = 13316 /* (C string xml, C string
                                        targetParentPath) */

Replaces the matching element values in the target list.

kActionTrackSetIdleFrequency = 2056 /* (long frequency) */

Allows changing the idle frequency, i.e., the time between successive calls to idle handlers, of sprite and text tracks. The range is from -1 to max unsigned long.

kActionQTVREnableHotSpot = 4101 /* long ID, Boolean enable */

Enables or disables a QuickTime VR hot spot by id.

kActionQTVRShowHotSpots = 4102 /* Boolean show */

Tells the QuickTime VR controller to show/hide all hots spots, same as clicking on the button in the controller.

kActionQTVRTranslateObject  = 4103 /* float xMove, float yMove *

Moves a QuickTime VR object in the direction specified by the parameters.

New Wired Operands

kOperandEventParameter = 26         /* short index */

Allows key and mouse event handlers to get parameters of the triggered event.

For the mouse:

        1 : where.h
        2 : where.v
        3 : modifiers

For the key:

        1 : where.h
        2 : where.v
        3 : modifiers
        4 : key
        5 : scancode
kOperandFreeMemory = 27

Returns the amount of memory free in the application heap.

kOperandNetworkStatus = 28

Returns the status code of the network connection:

    kQTNetworkStatusNoNetwork   = -2
    kQTNetworkStatusUncertain   = -1
    kQTNetworkStatusNotConnected = 0
    kQTNetworkStatusConnected   = 1
    kOperandMovieDuration  = 1029

Returns the duration of the target movie.

kOperandMovieTimeScale = 1030

Returns the timescale of the target movie.

kOperandMovieWidth = 1031

Returns the current width of the target movie.

kOperandMovieHeight = 1032

Returns the current height of the target movie.

kOperandMovieLoadState = 1033

Returns the load state of the target movie. <0 indicates an error.

    kMovieLoadStateLoading      = 1000
    kMovieLoadStatePlayable     = 10000
    kMovieLoadStatePlaythroughOK = 20000
    kMovieLoadStateComplete     = 100000L
    kOperandMovieTrackCount   = 1034

Returns the track count of the target movie.

kOperandTrackWidth = 2052

Returns the current width of the target track.

kOperandTrackHeight = 2053

Returns the current height of the target track.

kOperandTrackDuration = 2054

Returns the duration of the target track.

kOperandSpriteTrackSpriteIDAtPoint = 3094,/* short x, short y */

Returns the ID of the sprite that would be hit at the point where a mouse click occurred in the target sprite.

kOperandTextTrackEditable = 6144

Returns the current key entry state of the target text track.

kOperandTextTrackCopyText = 6145/* long startSelection, long endSelection                                     */

Returns the selection range as a string of the target text track.

kOperandTextTrackStartSelection = 6146

Returns the current starting selection point of the target text track.

kOperandTextTrackEndSelection = 6147

Returns the current ending selection point of the target text track.

kOperandTextTrackTextBoxLeft = 6148

Returns the left edge of the text box of target text track in text track coordinates.

kOperandTextTrackTextBoxTop = 6149

Returns the top edge of the text box of target text track in text track coordinates.

kOperandTextTrackTextBoxRight = 6150

Returns the right edge of the text box of target text track in text track coordinates.

kOperandTextTrackTextBoxBottom = 6151

Returns the bottom edge of the text box of the target text track in text track coordinates.

kOperandListCountElements = 7168/* (C string parentPath) */

Returns the number of elements in the target list.

kOperandListGetElementPathByIndex = 7169/* (C string parentPath, short index) */

Returns the name string of the element found a parentPath and the index of the target list.

kOperandListGetElementValue = 7170 /* (C string elementPath) */

Returns the value of the element at elementPath of the target list.

kOperandListCopyToXML= 7171     /* (C string parentPath, short                                     startIndex, short endIndex) */

Returns the selection of the target list as a XML-formatted string.

Note that the following math functions map directly to the math functions in the Macintosh interface, with the exception of kOperandDegreesToRadians and kOperandRadiansToDegrees, which are specific to QuickTime VR.

kOperandSin                 = 8192          /* float x  */
kOperandCos                 = 8193          /* float x  */
kOperandTan                 = 8194          /* float x  */
kOperandATan                = 8195          /* float x  */
kOperandATan2               = 8196      /* float y, float x */
kOperandDegreesToRadians    = 8197          /* float x */
kOperandRadiansToDegrees    = 8198          /* float x */
kOperandSquareRoot          = 8199          /* float x */
kOperandExponent            = 8200          /* float x */
kOperandLog                 = 8201          /* float x */
kOperandFlashTrackVariable  = 9216      /* [CString path, CString name] */

Returns the value of the Flash variable in the target Flash track.

kOperandSystemVersion = 30

This returns the Mac version. On Windows, it currently returns 0.

kOperandMovieIsActive = 1035
kOperandStringLength = 10240 /* (C string text) */
kOperandStringCompare = 10241 /* (C string aText, C string bText, Boolean
                                    caseSensitive, Boolan diacSensitive) */

Returns 0 for false, non-zero for true, indicating if the text is equivalent.

kOperandStringSubString = 10242 /* (C string text, long offset, long                                     length) */

Returns substring of text starting at 0-based offset 'offset' for length 'length'.

kOperandStringConcat = 10243     /* (C string aText, C string bText) */

Returns string produced by concatenating aText with bText.

kOperandQuickTimeVersionRegistered

This allows the scripter to verify that specific versions of QuickTime have been registered.

kOperandMovieName = 1036

Gets the target movie name, if any, stored in the user data as type 'plug' with data "moviename=theActualMovieName"

kOperandMovieID             = 1037

Gets the target movie name, if any, stored in the user data as type 'plug' with data "pmovieid=##".

kOperandTrackName           = 2055

Gets the target track name stored in track user data as type 'name'.

kOperandTrackID             = 2056

Gets the target track ID.

kOperandTrackIdleFrequency  = 2057

Gets the target track’s current idle frequency––only sprite and text currently.

kOperandSpriteName = 3095

Gets the target sprite name.

kOperandQTVRHotSpotsVisible = 4100

Returns whether the QuickTime VR controller is displaying the hot spots.

kOperandQTVRViewCenterH = 4101

Returns the view centerH of an QuickTime VR object controller.

kOperandQTVRViewCenterV = 4102

Returns the view centerH of an QuickTime VR object controller.