Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Completed Lab/QTSprites.c
////////// |
// |
// File: QTSprites.c |
// |
// Contains: QuickTime sprites support for QuickTime movies. |
// |
// Written by: ??? |
// Revised by: Deeje Cooley and Tim Monroe |
// Based (heavily!) on the existing MakeSpriteMovie.c code written by ???. |
// |
// Copyright: © 1997-1998 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <5> 03/17/00 rtm made changes to get things running under CarbonLib |
// <4> 09/30/98 rtm tweaked call to AddMovieResource to create single-fork movies |
// <3> 06/19/98 rtm moved to new routine names (e.g. SpriteMediaSetSpriteProperty) |
// <2> 04/09/98 rtm added sprite hit-testing |
// <1> 04/02/98 rtm first file; integrated existing code with shell framework |
// |
// This sample code creates 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 hit test sprites. 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. You would probably want to do something a bit |
// more interesting when the user clicks on a sprite. |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "QTSprites.h" |
////////// |
// |
// global variables |
// |
////////// |
Boolean gUseBackgroundPicture = true; // do we display a background picture? |
////////// |
// |
// QTSprites_InitWindowData |
// Do any sprite-specific initialization for the specified window. |
// |
////////// |
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); |
} |
////////// |
// |
// QTSprites_DumpWindowData |
// Get rid of any window-specific data for the sprite media handler. |
// |
////////// |
void QTSprites_DumpWindowData (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData = NULL; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData != NULL) |
DisposeHandle((Handle)myAppData); |
} |
////////// |
// |
// QTSprites_CreateSpritesMovie |
// Create a QuickTime movie containing a sprite track. |
// |
////////// |
OSErr QTSprites_CreateSpritesMovie (void) |
{ |
short myResRefNum = 0; |
short myResID = movieInDataForkResID; |
Movie myMovie = NULL; |
Track myTrack; |
Media myMedia; |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
StringPtr myPrompt = QTUtils_ConvertCToPascalString(kSpriteSavePrompt); |
StringPtr myFileName = QTUtils_ConvertCToPascalString(kSpriteSaveMovieFileName); |
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; |
////////// |
// |
// create a new movie file |
// |
////////// |
// prompt user for the destination file name |
QTFrame_PutFile(myPrompt, myFileName, &myFile, &myIsSelected, &myIsReplacing); |
if (!myIsSelected) |
goto bail; |
// create a movie file for the destination movie |
myErr = CreateMovieFile(&myFile, FOUR_CHAR_CODE('TVOD'), smSystemScript, myFlags, &myResRefNum, &myMovie); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// create the sprite track and media |
// |
////////// |
myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16), ((long)kSpriteTrackHeight << 16), kNoVolume); |
myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0); |
////////// |
// |
// create a key frame sample containing the sprites and all of their shared images |
// |
////////// |
// create a new, empty key frame sample |
myErr = QTNewAtomContainer(&mySample); |
if (myErr != noErr) |
goto bail; |
// specify transparency color for recompression |
myKeyColor.red = myKeyColor.green = myKeyColor.blue = 0xffff; // white |
// add images to the key frame sample - AddPICTImageToKeyFrameSample |
// will add the following atom data to the key frame atom container: |
// |
// kSpriteShareDataAtomType |
// kSpriteImagesContainerAtomType |
// kSpriteImageAtomType |
// kSpriteImageDataAtomType |
// |
// and optionally: |
// |
// kSpriteImageRegistrationAtomType |
// kSpriteImageNameAtomType |
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); |
////////// |
// |
// add samples to the sprite track's media |
// |
////////// |
// the code below will add to each kSpriteAtomType |
// sprite atom the following sprite property atoms: |
// |
// kSpritePropertyImageIndex |
// kSpritePropertyLayer |
// kSpritePropertyGraphicsMode |
// kSpritePropertyMatrix |
// kSpritePropertyVisible |
// kSpriteNameAtomType |
// kSpriteURLLinkAtomtype |
BeginMediaEdits(myMedia); |
// create |
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 DataCodec to perform the |
// compression; replace the call to the utility AddSpriteSampleToMedia with a call to the utility |
// AddCompressedSpriteSampleToMedia to do this |
AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, true, NULL); |
//AddCompressedSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration, true, zlibDataCompressorSubType, NULL); |
////////// |
// |
// 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++) { |
// remove existing atoms (which we used above |
// to create our key frame sample) from our key |
// frame sample atom container so we can re-use |
// the atom containers for our override samples |
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 |
// first remove previous children from our container |
// so we can re-add the new sprite property atoms |
QTRemoveChildren(mySpriteData, kParentAtomIsContainer); |
// change the icon location |
myIconLocation.h += myDelta; |
if (myIconLocation.h >= myIconMaxH ) { |
myIconLocation.h = myIconMaxH; |
myDelta = -myDelta; |
} |
if (myIconLocation.h <= myIconMinH ) { |
myIconLocation.h = myIconMinH; |
myDelta = -myDelta; |
} |
// change the sprite layer |
if (myDelta > 0) |
myLayer = 0; |
else |
myLayer = 3; |
// set the data for the sprite |
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); |
////////// |
// |
// 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); |
} |
////////// |
// |
// finish up |
// |
////////// |
// add the movie resource to the movie file |
myErr = AddMovieResource(myMovie, myResRefNum, &myResID, myFile.name); |
bail: |
free(myPrompt); |
free(myFileName); |
if (mySample != NULL) |
QTDisposeAtomContainer(mySample); |
if (mySpriteData != NULL) |
QTDisposeAtomContainer(mySpriteData); |
if (myResRefNum != 0) |
CloseMovieFile(myResRefNum); |
if (myMovie != NULL) |
DisposeMovie(myMovie); |
return(myErr); |
} |
////////// |
// |
// QTSprites_HitTestSprites |
// 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)QTFrame_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); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14