Common Files/SpriteUtilities.c

//////////
//
//  File:       SpriteUtilities.c
//
//  Contains:   Utilities for adding sprite tracks to QuickTime movies.
//
//  Written by: Sean Allen
//  Revised by: Chris Flick and Tim Monroe
//
//  Copyright:  © 1997-1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <4>      11/10/00    rtm     general clean-up to bring this file into conformance with style of other
//                                  sample code
//     <3>      03/27/98    rtm     added error checking to AddPICTImageToKeyFrameSample to prevent crashes
//                                  if PICT resources not found
//     <2>      03/27/98    cf      further fixes for Windows compiles
//     <1>      03/26/98    rtm     made fixes for Windows compiles
//     
//
//////////
 
#ifndef _SPRITEUTILITIES_
#include "SpriteUtilities.h"
#endif
 
 
//////////
//
// SpriteUtils_SetSpriteData
// Set sprite properties for non-NULL parameters, overriding or adding atoms as necessary.
//
// Keep in mind that all data in an atom container must be in big-endian format.
//
//////////
 
OSErr SpriteUtils_SetSpriteData (QTAtomContainer theSprite, Point *theLocation, short *theVisible, short *theLayer, short *theImageIndex, ModifierTrackGraphicsModeRecord *theGraphicsMode, StringPtr theSpriteName, QTAtomContainer theActionAtoms)
{
    QTAtom              myPropertyAtom;
    OSErr               myErr = noErr;
    
    // set the sprite location data
    if (theLocation != NULL) {
        MatrixRecord    myMatrix;
        
        SetIdentityMatrix(&myMatrix);
        myMatrix.matrix[2][0] = ((long)theLocation->h << 16);
        myMatrix.matrix[2][1] = ((long)theLocation->v << 16);
        EndianUtils_MatrixRecord_NtoB(&myMatrix);
 
        myPropertyAtom = QTFindChildByIndex(theSprite, kParentAtomIsContainer, kSpritePropertyMatrix, 1, NULL);
        if (myPropertyAtom == 0)
            myErr = QTInsertChild(theSprite, kParentAtomIsContainer, kSpritePropertyMatrix, 1, 0, sizeof(MatrixRecord), &myMatrix, NULL);
        else
            myErr = QTSetAtomData(theSprite, myPropertyAtom, sizeof(MatrixRecord), &myMatrix);
            
        if (myErr != noErr)
            goto bail;
    }
    
    // set the sprite visibility state
    if (theVisible != NULL) {
        short           myVisible = *theVisible;
        
        myVisible = EndianS16_NtoB(myVisible);
        
        myPropertyAtom = QTFindChildByIndex(theSprite, kParentAtomIsContainer, kSpritePropertyVisible, 1, NULL);
        if (myPropertyAtom == 0)
            myErr = QTInsertChild(theSprite, kParentAtomIsContainer, kSpritePropertyVisible, 1, 0, sizeof(short), &myVisible, NULL);
        else
            myErr = QTSetAtomData(theSprite, myPropertyAtom, sizeof(short), &myVisible);
            
        if (myErr != noErr)
            goto bail;
    }
    
    // set the sprite layer
    if (theLayer != NULL) {
        short           myLayer = *theLayer;
        
        myLayer = EndianS16_NtoB(myLayer);
 
        myPropertyAtom = QTFindChildByIndex(theSprite, 0, kSpritePropertyLayer, 1, NULL);
        if (myPropertyAtom == 0)
            myErr = QTInsertChild(theSprite, 0, kSpritePropertyLayer, 1, 0, sizeof(short), &myLayer, NULL);
        else
            myErr = QTSetAtomData(theSprite, myPropertyAtom, sizeof(short), &myLayer);
            
        if (myErr != noErr)
            goto bail;
    }
    
    // set the sprite image index
    if (theImageIndex != NULL) {
        short           myImageIndex = *theImageIndex;
 
        myImageIndex = EndianS16_NtoB(myImageIndex);
        
        myPropertyAtom = QTFindChildByIndex(theSprite, kParentAtomIsContainer, kSpritePropertyImageIndex, 1, NULL);
        if (myPropertyAtom == 0)
            myErr = QTInsertChild(theSprite, kParentAtomIsContainer, kSpritePropertyImageIndex, 1, 0, sizeof(short), &myImageIndex, NULL);
        else
            myErr = QTSetAtomData(theSprite, myPropertyAtom, sizeof(short), &myImageIndex);
            
        if (myErr != noErr)
            goto bail;
    }
    
    // set the sprite graphics mode
    if (theGraphicsMode != NULL) {
        ModifierTrackGraphicsModeRecord     myGraphicsMode;
        
        myGraphicsMode.graphicsMode = EndianU32_NtoB(theGraphicsMode->graphicsMode);
        myGraphicsMode.opColor.red = EndianU16_NtoB(theGraphicsMode->opColor.red);
        myGraphicsMode.opColor.green = EndianU16_NtoB(theGraphicsMode->opColor.green);
        myGraphicsMode.opColor.blue = EndianU16_NtoB(theGraphicsMode->opColor.blue);
 
        myPropertyAtom = QTFindChildByIndex(theSprite, kParentAtomIsContainer, kSpritePropertyGraphicsMode, 1, NULL);
        if (myPropertyAtom == 0)
            myErr = QTInsertChild(theSprite, kParentAtomIsContainer, kSpritePropertyGraphicsMode, 1, 0, sizeof(myGraphicsMode), &myGraphicsMode, NULL);
        else
            myErr = QTSetAtomData(theSprite, myPropertyAtom, sizeof(myGraphicsMode), &myGraphicsMode);
            
        if (myErr != noErr)
            goto bail;
    }
    
    // set the sprite name
    if (theSpriteName != NULL) {
        QTAtom      mySpriteNameAtom;
        
        mySpriteNameAtom = QTFindChildByIndex(theSprite, kParentAtomIsContainer, kSpriteNameAtomType, 1, NULL);
        if (mySpriteNameAtom == 0)
            myErr = QTInsertChild(theSprite, kParentAtomIsContainer, kSpriteNameAtomType, 1, 0, theSpriteName[0] + 1, theSpriteName, NULL);
        else
            myErr = QTSetAtomData(theSprite, mySpriteNameAtom, theSpriteName[0] + 1, theSpriteName);
 
        if (myErr != noErr)
            goto bail;
    }
    
    // set the action atoms
    if (theActionAtoms != NULL)
        myErr = QTInsertChildren(theSprite, kParentAtomIsContainer, theActionAtoms);
    
bail:
    if ((myErr != noErr) && (theSprite != NULL))
        QTRemoveChildren(theSprite, 0);
 
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AddSpriteToSample
// Add a sprite to a sample.
//
//////////
 
OSErr SpriteUtils_AddSpriteToSample (QTAtomContainer theSample, QTAtomContainer theSprite, QTAtomID theSpriteID)
{
    QTAtom              mySpriteAtom = 0;
    OSErr               myErr = paramErr;
    
    // see if the sample already contains a sprite atom of the specified ID
    mySpriteAtom = QTFindChildByID(theSample, kParentAtomIsContainer, kSpriteAtomType, theSpriteID, NULL);
    if (mySpriteAtom != 0)
        goto bail;
    
    // here, the index 0 means to append the sprite to the sample
    myErr = QTInsertChild(theSample, kParentAtomIsContainer, kSpriteAtomType, theSpriteID, 0, 0, NULL, &mySpriteAtom);
    if (myErr != noErr)
        goto bail;
 
    myErr = QTInsertChildren(theSample, mySpriteAtom, theSprite);
    
bail:
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AddSpriteSampleToMedia
// Add a sprite sample to a sprite track's media.
//
//////////
 
OSErr SpriteUtils_AddSpriteSampleToMedia (Media theMedia, QTAtomContainer theSample, TimeValue theDuration, Boolean isKeyFrame, TimeValue *theSampleTime)
{
    SampleDescriptionHandle     mySampleDesc = NULL;
    OSErr                       myErr = noErr;
    
    mySampleDesc = (SampleDescriptionHandle)NewHandleClear(sizeof(SpriteDescription));
    if (mySampleDesc == NULL) {
        myErr = MemError();
        goto bail;
    }
    
    myErr = AddMediaSample(theMedia,
                            (Handle)theSample,
                            0,
                            GetHandleSize(theSample),
                            theDuration,
                            mySampleDesc,
                            1,
                            (short)(isKeyFrame ? 0 : mediaSampleNotSync),
                            theSampleTime);
                            
bail:
    if (mySampleDesc != NULL)
        DisposeHandle((Handle) mySampleDesc);
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AddCompressedSpriteSampleToMedia
// Add a compressed sprite sample to a sprite track's media.
//
//////////
 
OSErr SpriteUtils_AddCompressedSpriteSampleToMedia (Media theMedia, QTAtomContainer theSample, TimeValue theDuration, Boolean isKeyFrame, OSType theDataCompressorType, TimeValue *theSampleTime)
{
    SpriteDescriptionHandle     mySampleDesc = NULL;
    Handle                      myCompressedSample = NULL;
    ComponentInstance           myComponent = NULL;
    OSErr                       myErr = noErr;
    
    myErr = OpenADefaultComponent(DataCompressorComponentType, theDataCompressorType, &myComponent);
    if (myErr != noErr)
        goto bail;
 
    mySampleDesc = (SpriteDescriptionHandle)NewHandleClear(sizeof(SpriteDescription));
    if (mySampleDesc == NULL) {
        myErr = MemError();
        goto bail;
    }
    
    if (myComponent != NULL) {
        UInt32                  myCompressBufferSize, myActualCompressedSize, myDecompressSlop = 0;
        UInt32                  myUncompressedSize;
        SignedByte              mySaveState = HGetState(theSample);
        
        myErr = (OSErr)DataCodecGetCompressBufferSize(myComponent, GetHandleSize(theSample), &myCompressBufferSize);
        if (myErr != noErr)
            goto bail;
        
        myCompressedSample = NewHandle(sizeof(UInt32) + myCompressBufferSize);
        myErr = MemError();
        if (myErr != noErr)
            goto bail;
        
        HLockHi(theSample);
        HLockHi(myCompressedSample);
        myErr = (OSErr)DataCodecCompress(myComponent, 
                                        *theSample, 
                                        GetHandleSize(theSample), 
                                        *myCompressedSample + sizeof(UInt32),       // room for size at beginning
                                        myCompressBufferSize, 
                                        &myActualCompressedSize,
                                        &myDecompressSlop);
        
        HSetState(theSample, mySaveState);
        HUnlock(myCompressedSample);
        
        if (myErr != noErr)
            goto bail;
        
        SetHandleSize(myCompressedSample, sizeof(UInt32) + myActualCompressedSize);
        myErr = MemError();
        if (myErr != noErr)
            goto bail;
 
        (**mySampleDesc).decompressorType = EndianU32_NtoB(theDataCompressorType);
    
        myUncompressedSize = GetHandleSize(theSample);
        (*(UInt32*) *myCompressedSample) = EndianU32_NtoB(myUncompressedSize);      // add uncompressed size at beginning
        
        myErr = AddMediaSample(theMedia,
                                (Handle)myCompressedSample,
                                0,
                                GetHandleSize(myCompressedSample),
                                theDuration,
                                (SampleDescriptionHandle)mySampleDesc,
                                1,
                                (short)(isKeyFrame ? 0 : mediaSampleNotSync),
                                theSampleTime);
    } else {
        myErr = AddMediaSample(theMedia,
                                (Handle)theSample,
                                0,
                                GetHandleSize(theSample),
                                theDuration,
                                (SampleDescriptionHandle)mySampleDesc,
                                1,
                                (short)(isKeyFrame ? 0 : mediaSampleNotSync),
                                theSampleTime);
    }
    
bail:
    if (myCompressedSample != NULL)
        DisposeHandle(myCompressedSample);
        
    if (mySampleDesc != NULL)
        DisposeHandle((Handle)mySampleDesc);
        
    if (myComponent != NULL)
        CloseComponent(myComponent);
    
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AddPICTImageToKeyFrameSample
// Compress a PICT with the animation compressor and add the image data to a sprite key sample's images container atom.
//
//////////
 
OSErr SpriteUtils_AddPICTImageToKeyFrameSample (QTAtomContainer theKeySample, short thePictID, RGBColor *theKeyColor, QTAtomID theID, FixedPoint *theRegistrationPoint, StringPtr theImageName)
{
    PicHandle               myPicture = NULL;
    Handle                  myCompressedPicture = NULL;
    ImageDescriptionHandle  myImageDesc = NULL;
    OSErr                   myErr = noErr;
    
    // get picture from resource
    myPicture = (PicHandle)GetPicture(thePictID);
    if (myPicture == NULL)
        myErr = resNotFound;
 
    if (myErr != noErr)
        goto bail;
    
    DetachResource((Handle)myPicture);
    
    // convert it to image data compressed by the animation compressor
    myErr = ICUtils_RecompressPictureWithTransparency(myPicture, theKeyColor, NULL, &myImageDesc, &myCompressedPicture);
    if (myErr != noErr)
        goto bail;
 
    // add it to the key sample
    HLock(myCompressedPicture);
    myErr = SpriteUtils_AddCompressedImageToKeyFrameSample(theKeySample, myImageDesc, GetHandleSize(myCompressedPicture), *myCompressedPicture, theID, theRegistrationPoint, theImageName);
    
bail:
    if (myPicture != NULL)
        KillPicture(myPicture);
        
    if (myCompressedPicture != NULL)
        DisposeHandle(myCompressedPicture);
        
    if (myImageDesc != NULL)
        DisposeHandle((Handle)myImageDesc);
        
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AddCompressedImageToKeyFrameSample
// Add compressed image data to a sprite key sample's images container atom.
//
//////////
 
OSErr SpriteUtils_AddCompressedImageToKeyFrameSample (QTAtomContainer theKeySample, ImageDescriptionHandle theImageDesc, long theDataSize, Ptr theCompressedDataPtr, QTAtomID theImageID, FixedPoint *theRegistrationPoint, StringPtr theImageName)
{
    Handle                      myImageData = NULL;
    QTAtom                      myDefaultsAtom, myImagesContainerAtom, myImageAtom;
    ImageDescriptionHandle      myImageDesc = NULL;
    OSErr                       myErr = noErr;
 
#if TARGET_RT_LITTLE_ENDIAN
    myImageDesc = (ImageDescriptionHandle)NewHandle(GetHandleSize((Handle)theImageDesc));
    BlockMoveData(*theImageDesc, *myImageDesc, GetHandleSize((Handle)theImageDesc));
    EndianUtils_ImageDescription_NtoB(myImageDesc);
#else
    myImageDesc = theImageDesc;         // already is big endian
#endif
 
    // append compressed picture data to myImageDesc to obtain sprite image data
    myImageData = NewHandle(0);
    myErr = MemError();
    if (myErr != noErr)
        goto bail;
    
    myErr = HandAndHand((Handle)myImageDesc, myImageData);
    if (myErr != noErr)
        goto bail;
    
    myErr = PtrAndHand(theCompressedDataPtr, myImageData, theDataSize);
    if (myErr != noErr)
        goto bail;
    
    myDefaultsAtom = QTFindChildByIndex(theKeySample, 0, kSpriteSharedDataAtomType, 1, NULL);
    if (myDefaultsAtom == 0) {
        myErr = QTInsertChild(theKeySample, kParentAtomIsContainer, kSpriteSharedDataAtomType, 1, 0, 0, NULL, &myDefaultsAtom);
        if (myErr != noErr)
            goto bail;
    }
        
    myImagesContainerAtom = QTFindChildByIndex(theKeySample, myDefaultsAtom, kSpriteImagesContainerAtomType, 1, NULL);
    if (myImagesContainerAtom == 0) {
        myErr = QTInsertChild(theKeySample, myDefaultsAtom, kSpriteImagesContainerAtomType, 1, 0, 0, NULL, &myImagesContainerAtom);
        if (myErr != noErr)
            goto bail;
    }
 
    myErr = QTInsertChild(theKeySample, myImagesContainerAtom, kSpriteImageAtomType, theImageID, 0, 0, NULL, &myImageAtom);
    if (myErr != noErr)
        goto bail;
 
    HLock(myImageData);
    myErr = QTInsertChild(theKeySample, myImageAtom, kSpriteImageDataAtomType, 1, 0, GetHandleSize(myImageData), *myImageData, NULL);
    if (myErr != noErr)
        goto bail;
    HUnlock(myImageData);
    
    if (theRegistrationPoint != NULL) {
        FixedPoint          myRegistrationPoint;
        
        myRegistrationPoint.x = EndianS32_NtoB(theRegistrationPoint->x);
        myRegistrationPoint.y = EndianS32_NtoB(theRegistrationPoint->y);
        
        myErr = QTInsertChild(theKeySample, myImageAtom, kSpriteImageRegistrationAtomType, 1, 0, sizeof(myRegistrationPoint), &myRegistrationPoint, NULL);
        if (myErr != noErr)
            goto bail;
    } else {
        FixedPoint          myRegistrationPoint = { 0, 0 };     // flipping {0,0} doesn't change anything so we don't flip
        
        myErr = QTInsertChild(theKeySample, myImageAtom, kSpriteImageRegistrationAtomType, 1, 0, sizeof(myRegistrationPoint), &myRegistrationPoint, NULL);
        if (myErr != noErr)
            goto bail;
    }
    
    if (theImageName != NULL) {
        myErr = QTInsertChild(theKeySample, myImageAtom, kSpriteImageNameAtomType, 1, 0, theImageName[0], &theImageName[1], NULL);
        if (myErr != noErr)
            goto bail;
    }
 
bail:
#if TARGET_RT_LITTLE_ENDIAN
    if (myImageDesc != NULL)
        DisposeHandle((Handle)myImageDesc);
#else
    // myImageDesc is still theImageDesc, so don't dispose of it
#endif
 
    if (myImageData != NULL)
        DisposeHandle(myImageData);
        
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AssignImageGroupIDsToKeyFrame
// Assign image group IDs to the images in a key frame sample.
//
//////////
 
OSErr SpriteUtils_AssignImageGroupIDsToKeyFrame (QTAtomContainer theKeySample)
{
    QTAtom                      myDefaultsAtom, myImagesContainerAtom;
    ImageDescriptionHandle      myFirstImageDesc = NULL;
    ImageDescriptionHandle      mySecondImageDesc = NULL;
    short                       myFirstIndex, mySecondIndex, myNumImages;
    CodecType                   myFirstImageType, mySecondImageType;
    long                        myGroupID = 0, myTestID;
    OSErr                       myErr = noErr;
    
    myDefaultsAtom = QTFindChildByIndex(theKeySample, 0, kSpriteSharedDataAtomType, 1, NULL);
    if (myDefaultsAtom == 0)
        goto bail;
    
    myImagesContainerAtom = QTFindChildByIndex(theKeySample, myDefaultsAtom, kSpriteImagesContainerAtomType, 1, NULL);
    if (myImagesContainerAtom == 0)
        goto bail;
    
    myFirstImageDesc = (ImageDescriptionHandle)NewHandle(0);
    if (myFirstImageDesc == NULL) {
        myErr = memFullErr;
        goto bail; 
    }
 
    mySecondImageDesc = (ImageDescriptionHandle)NewHandle(0);
    if (mySecondImageDesc == NULL) {
        myErr = memFullErr;
        goto bail; 
    }
 
    myNumImages = QTCountChildrenOfType(theKeySample, myImagesContainerAtom, kSpriteImageAtomType);
    for (myFirstIndex = 1; myFirstIndex <= myNumImages; myFirstIndex++) {
        myErr = SpriteUtils_SetImageGroupID(theKeySample, myImagesContainerAtom, myFirstIndex, 0);
        if (myErr != noErr)
            goto bail;
    }
    
    for (myFirstIndex = 1; myFirstIndex <= (myNumImages - 1); myFirstIndex++) {
        myErr = SpriteUtils_GetImageGroupID(theKeySample, myImagesContainerAtom, myFirstIndex, &myTestID);
        if (myErr != noErr)
            goto bail;
        
        if (myTestID == 0) {
            myGroupID++;
            myErr = SpriteUtils_SetImageGroupID(theKeySample, myImagesContainerAtom, myFirstIndex, myGroupID);
            if (myErr != noErr)
                goto bail;
            
            myErr = SpriteUtils_GetImageDescription(theKeySample, myImagesContainerAtom, myFirstIndex, myFirstImageDesc);
            myFirstImageType = (**myFirstImageDesc).cType;
            if (myErr != noErr)
                goto bail;
 
            for (mySecondIndex = (myFirstIndex + 1); mySecondIndex <= myNumImages; mySecondIndex++) {
                myErr = SpriteUtils_GetImageGroupID(theKeySample, myImagesContainerAtom, mySecondIndex, &myTestID);
                if (myErr != noErr)
                    goto bail;
                    
                if (myTestID == 0) {
                    myErr = SpriteUtils_GetImageDescription(theKeySample, myImagesContainerAtom, mySecondIndex, mySecondImageDesc);
                    if (myErr != noErr)
                        goto bail;
                    mySecondImageType = (**mySecondImageDesc).cType;
                
                    if (myFirstImageType == mySecondImageType) {
                        ImageSequence   mySeqID;
                        Boolean         isEquivalent;
                        
                        myErr = DecompressSequenceBegin(&mySeqID, myFirstImageDesc, NULL, NULL, NULL, NULL, ditherCopy, (RgnHandle)NULL,  0, codecNormalQuality, anyCodec);
                        if (myErr != noErr)
                            goto bail;
                                     
                        CDSequenceEquivalentImageDescription(mySeqID, mySecondImageDesc, &isEquivalent);
                        CDSequenceEnd(mySeqID);
                        
                        if (isEquivalent) {
                            myErr = SpriteUtils_SetImageGroupID(theKeySample, myImagesContainerAtom, mySecondIndex, myGroupID);
                            if (myErr != noErr)
                                goto bail;
                        }
                    }
                }
            }
        }
    }
    
    // assign an ID to the last image
    myErr = SpriteUtils_GetImageGroupID(theKeySample, myImagesContainerAtom, myNumImages, &myTestID);
    if (myErr != noErr)
        goto bail;
        
    if (myTestID == 0) {
        myGroupID++;
        myErr = SpriteUtils_SetImageGroupID(theKeySample, myImagesContainerAtom, myNumImages, myGroupID);
    }
    
bail:
    if (myFirstImageDesc != NULL)
        DisposeHandle((Handle)myFirstImageDesc);
        
    if (mySecondImageDesc != NULL)
        DisposeHandle((Handle)mySecondImageDesc);
        
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_AssignImageGroupIDsToKeyFrame
// Assign image group IDs to the images in a key frame sample.
//
//////////
 
static OSErr SpriteUtils_GetImageDescription (QTAtomContainer theKeySample, QTAtom theImagesContainerAtom, short theImageIndex, ImageDescriptionHandle theImageDesc)
{
    QTAtom                      myImageAtom, myImageDataAtom;
    UInt8                       mySaveState;
    UInt32                      mySize;
    OSErr                       myErr = noErr;
 
    myImageAtom = QTFindChildByIndex(theKeySample, theImagesContainerAtom, kSpriteImageAtomType, theImageIndex, NULL);
    if (myImageAtom == 0)   { 
        myErr = cannotFindAtomErr; 
        goto bail;
    }
 
    myImageDataAtom = QTFindChildByIndex(theKeySample, myImageAtom, kSpriteImageDataAtomType, 1, NULL);
    if (myImageDataAtom == 0)   { 
        myErr = cannotFindAtomErr; 
        goto bail;
    }
 
    mySaveState = HGetState((Handle)theImageDesc);
    HUnlock((Handle)theImageDesc);
 
    // copy the data (ImageDescription followed by image data) to a handle
    myErr = QTCopyAtomDataToHandle(theKeySample, myImageDataAtom, (Handle)theImageDesc);
    if (myErr != noErr)
        goto bail;
 
    mySize = EndianU32_BtoN((**theImageDesc).idSize);
 
    // pull off anything following the image description (& its color table, if any, and any image description extensions)
    SetHandleSize((Handle)theImageDesc, mySize);
 
#if TARGET_RT_LITTLE_ENDIAN
    EndianUtils_ImageDescription_BtoN(theImageDesc);
#endif
 
    HSetState((Handle)theImageDesc, mySaveState);
    myErr = MemError();
    
bail:
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_SetImageGroupID
// Set the group ID of an image group.
//
//////////
 
static OSErr SpriteUtils_SetImageGroupID (QTAtomContainer theKeySample, QTAtom theImagesContainerAtom, short theImageIndex, long theGroupID)
{
    QTAtom          myImageAtom, myImageGroupAtom;
    OSErr           myErr = noErr;
    
    myImageAtom = QTFindChildByIndex(theKeySample, theImagesContainerAtom, kSpriteImageAtomType, theImageIndex, NULL);
    if (myImageAtom == 0)   { 
        myErr = cannotFindAtomErr;
        goto bail;
    }
 
    myImageGroupAtom = QTFindChildByIndex(theKeySample, myImageAtom, kSpriteImageGroupIDAtomType, 1, NULL);
    if (myImageGroupAtom == 0) {
        myErr = QTInsertChild(theKeySample, myImageAtom, kSpriteImageGroupIDAtomType, 1, 1, 0, NULL, &myImageGroupAtom);
        if (myErr != noErr)
            goto bail;
    }
 
    theGroupID = EndianU32_NtoB(theGroupID);
    myErr = QTSetAtomData(theKeySample, myImageGroupAtom, sizeof(theGroupID), &theGroupID);
 
bail:
    return(myErr);
}
 
 
//////////
//
// SpriteUtils_GetImageGroupID
// Get the group ID of an image group.
//
//////////
 
static OSErr SpriteUtils_GetImageGroupID (QTAtomContainer theKeySample, QTAtom theImagesContainerAtom, short theImageIndex, long *theGroupID)
{
    QTAtom          myImageAtom, myImageGroupAtom;
    OSErr           myErr = noErr;
 
    myImageAtom = QTFindChildByIndex(theKeySample, theImagesContainerAtom, kSpriteImageAtomType, theImageIndex, NULL);
    if (myImageAtom == 0)   { 
        myErr = cannotFindAtomErr;
        goto bail;
    }
 
    myImageGroupAtom = QTFindChildByIndex(theKeySample, myImageAtom, kSpriteImageGroupIDAtomType, 1, NULL);
    
    if (myImageGroupAtom == 0)
        *theGroupID = 0;
    else {
        myErr = QTCopyAtomDataToPtr(theKeySample, myImageGroupAtom, false, sizeof(*theGroupID), (Ptr)theGroupID, NULL);
        if (myErr != noErr)
            goto bail;
 
        *theGroupID = EndianU32_BtoN(*theGroupID);      // return native endian long
    }
    
bail:
    return(myErr);
}