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.
Common Files/EffectsUtilities.c
////////// |
// |
// File: EffectsUtilities.c |
// |
// Contains: Some utilities for working with QuickTime video effects. |
// |
// Written by: Tim Monroe |
// |
// Copyright: © 2001 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <1> 07/10/01 rtm first file |
// |
// |
////////// |
#ifndef __EFFECTSUTILITIES__ |
#include "EffectsUtilities.h" |
#endif |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Effects utilities. |
// |
// Use these functions to set up and run QuickTime video effects. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// EffectsUtils_CreateEffectDescription |
// Create an effect description for zero, one, two, or three sources. |
// |
// The effect description specifies which video effect is desired and the parameters for that effect. |
// It also describes the source(s) for the effect. An effect description is simply an atom container |
// that holds atoms with the appropriate information. |
// |
// Note that because we are creating an atom container, we must pass big-endian data (hence the calls |
// to EndianU32_NtoB). |
// |
// The caller is responsible for disposing of the returned atom container, by calling QTDisposeAtomContainer. |
// |
////////// |
QTAtomContainer EffectsUtils_CreateEffectDescription (OSType theEffectType, OSType theSourceName1, OSType theSourceName2, OSType theSourceName3) |
{ |
QTAtomContainer myEffectDesc = NULL; |
OSType myType = EndianU32_NtoB(theEffectType); |
OSErr myErr = noErr; |
// create a new, empty effect description |
myErr = QTNewAtomContainer(&myEffectDesc); |
if (myErr != noErr) |
goto bail; |
// create the effect ID atom: the atom type is kParameterWhatName, and the atom ID is kParameterWhatID |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
// add the first source |
if (theSourceName1 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName1); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 1, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
} |
// add the second source |
if (theSourceName2 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName2); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
} |
// add the third source |
if (theSourceName3 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName3); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 3, 0, sizeof(myType), &myType, NULL); |
} |
bail: |
return(myEffectDesc); |
} |
////////// |
// |
// EffectsUtils_GetTypeFromEffectDescription |
// Given an effect description, return the type of the effect it describes. |
// |
////////// |
OSErr EffectsUtils_GetTypeFromEffectDescription (QTAtomContainer theEffectDesc, OSType *theEffectType) |
{ |
QTAtom myEffectAtom = 0; |
long myEffectTypeSize = 0; |
Ptr myEffectTypePtr = NULL; |
OSErr myErr = noErr; |
if ((theEffectDesc == NULL) || (theEffectType == NULL)) |
return(paramErr); |
myEffectAtom = QTFindChildByIndex(theEffectDesc, kParentAtomIsContainer, kParameterWhatName, kParameterWhatID, NULL); |
if (myEffectAtom != 0) { |
myErr = QTLockContainer(theEffectDesc); |
if (myErr != noErr) |
goto bail; |
myErr = QTGetAtomDataPtr(theEffectDesc, myEffectAtom, &myEffectTypeSize, &myEffectTypePtr); |
if (myErr != noErr) |
goto bail; |
if (myEffectTypeSize != sizeof(OSType)) { |
myErr = paramErr; |
goto bail; |
} |
*theEffectType = *(OSType *)myEffectTypePtr; |
*theEffectType = EndianU32_BtoN(*theEffectType); // because the data is read from an atom container |
myErr = QTUnlockContainer(theEffectDesc); |
} |
bail: |
return(myErr); |
} |
////////// |
// |
// EffectsUtils_AddTrackReferenceToInputMap |
// Add a track reference to the specified input map. |
// |
////////// |
OSErr EffectsUtils_AddTrackReferenceToInputMap (QTAtomContainer theInputMap, Track theTrack, Track theSrcTrack, OSType theSrcName) |
{ |
QTAtom myInputAtom; |
long myRefIndex; |
OSType myType; |
OSErr myErr = noErr; |
myErr = AddTrackReference(theTrack, theSrcTrack, kTrackReferenceModifier, &myRefIndex); |
if (myErr != noErr) |
goto bail; |
// add a reference atom to the input map |
myErr = QTInsertChild(theInputMap, kParentAtomIsContainer, kTrackModifierInput, myRefIndex, 0, 0, NULL, &myInputAtom); |
if (myErr != noErr) |
goto bail; |
// add two child atoms to the parent reference atom |
myType = EndianU32_NtoB(kTrackModifierTypeImage); |
myErr = QTInsertChild(theInputMap, myInputAtom, kTrackModifierType, 1, 0, sizeof(myType), &myType, NULL); |
if (myErr != noErr) |
goto bail; |
myType = EndianU32_NtoB(theSrcName); |
myErr = QTInsertChild(theInputMap, myInputAtom, kEffectDataSourceType, 1, 0, sizeof(myType), &myType, NULL); |
bail: |
return(myErr); |
} |
////////// |
// |
// EffectsUtils_MakeSampleDescription |
// Return a new image description with default and specified values. |
// |
////////// |
ImageDescriptionHandle EffectsUtils_MakeSampleDescription (OSType theEffectType, short theWidth, short theHeight) |
{ |
ImageDescriptionHandle mySampleDesc = NULL; |
#if USES_MAKE_IMAGE_DESC_FOR_EFFECT |
OSErr myErr = noErr; |
// create a new sample description |
myErr = MakeImageDescriptionForEffect(theEffectType, &mySampleDesc); |
if (myErr != noErr) |
return(NULL); |
#else |
// create a new sample description |
mySampleDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); |
if (mySampleDesc == NULL) |
return(NULL); |
// fill in the fields of the sample description |
(**mySampleDesc).cType = theEffectType; |
(**mySampleDesc).idSize = sizeof(ImageDescription); |
(**mySampleDesc).hRes = 72L << 16; |
(**mySampleDesc).vRes = 72L << 16; |
(**mySampleDesc).frameCount = 1; |
(**mySampleDesc).depth = 0; |
(**mySampleDesc).clutID = -1; |
#endif |
(**mySampleDesc).vendor = kAppleManufacturer; |
(**mySampleDesc).temporalQuality = codecNormalQuality; |
(**mySampleDesc).spatialQuality = codecNormalQuality; |
(**mySampleDesc).width = theWidth; |
(**mySampleDesc).height = theHeight; |
return(mySampleDesc); |
} |
////////// |
// |
// EffectsUtils_GetEffectDescFromQFXFile |
// Open the specified effects parameter file and get the effect description in it. |
// |
////////// |
QTAtomContainer EffectsUtils_GetEffectDescFromQFXFile (FSSpec *theFSSpec) |
{ |
Handle myEffectDesc = NULL; |
short myRefNum = 0; |
long mySize = 0L; |
OSType myType = 0L; |
long myAtomHeader[2]; |
OSErr myErr = noErr; |
myErr = FSpOpenDF(theFSSpec, fsRdPerm, &myRefNum); |
if (myErr != noErr) |
goto bail; |
SetFPos(myRefNum, fsFromStart, 0); |
while ((myErr == noErr) && (myEffectDesc == NULL)) { |
// read the atom header at the current file position |
mySize = sizeof(myAtomHeader); |
myErr = FSRead(myRefNum, &mySize, myAtomHeader); |
if (myErr != noErr) |
goto bail; |
mySize = EndianU32_BtoN(myAtomHeader[0]) - sizeof(myAtomHeader); |
myType = EndianU32_BtoN(myAtomHeader[1]); |
if (myType == FOUR_CHAR_CODE('qtfx')) { |
myEffectDesc = NewHandleClear(mySize); |
if (myEffectDesc == NULL) |
goto bail; |
myErr = FSRead(myRefNum, &mySize, *myEffectDesc); |
} else { |
SetFPos(myRefNum, fsFromMark, mySize); |
} |
} |
bail: |
return((QTAtomContainer)myEffectDesc); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// General imaging utilities. |
// |
// Use these functions to draw pictures into GWorlds and do other imaging operations. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// EffectsUtils_GetPictResourceAsGWorld |
// Create a new GWorld of the specified size and bit depth; then draw the specified PICT resource into it. |
// The new GWorld is returned through the theGW parameter. |
// |
////////// |
OSErr EffectsUtils_GetPictResourceAsGWorld (short theResID, short theWidth, short theHeight, short theDepth, GWorldPtr *theGW) |
{ |
PicHandle myHandle = NULL; |
PixMapHandle myPixMap = NULL; |
CGrafPtr mySavedPort; |
GDHandle mySavedDevice; |
Rect myRect; |
OSErr myErr = noErr; |
// get the current drawing environment |
GetGWorld(&mySavedPort, &mySavedDevice); |
// read the specified PICT resource from the application's resource file |
myHandle = GetPicture(theResID); |
if (myHandle == NULL) { |
myErr = ResError(); |
if (myErr == noErr) |
myErr = resNotFound; |
goto bail; |
} |
// set the size of the GWorld |
MacSetRect(&myRect, 0, 0, theWidth, theHeight); |
// allocate a new GWorld |
myErr = QTNewGWorld(theGW, theDepth, &myRect, NULL, NULL, kICMTempThenAppMemory); |
if (myErr != noErr) |
goto bail; |
SetGWorld(*theGW, NULL); |
// get a handle to the offscreen pixel image and lock it |
myPixMap = GetGWorldPixMap(*theGW); |
LockPixels(myPixMap); |
EraseRect(&myRect); |
DrawPicture(myHandle, &myRect); |
if (myPixMap != NULL) |
UnlockPixels(myPixMap); |
bail: |
// restore the previous port and device |
SetGWorld(mySavedPort, mySavedDevice); |
if (myHandle != NULL) |
ReleaseResource((Handle)myHandle); |
return(myErr); |
} |
////////// |
// |
// EffectsUtils_AddVideoTrackFromGWorld |
// Add to the specified movie a video track for the specified picture resource. |
// |
////////// |
OSErr EffectsUtils_AddVideoTrackFromGWorld (Movie *theMovie, GWorldPtr theGW, Track *theSourceTrack, long theStartTime, TimeValue theDuration, short theWidth, short theHeight) |
{ |
Media myMedia; |
ImageDescriptionHandle mySampleDesc = NULL; |
Rect myRect; |
Rect myRect2; |
Rect myRect3; |
long mySize; |
Handle myData = NULL; |
Ptr myDataPtr = NULL; |
GWorldPtr myGWorld = NULL; |
CGrafPtr mySavedPort = NULL; |
GDHandle mySavedGDevice = NULL; |
PicHandle myHandle = NULL; |
PixMapHandle mySrcPixMap = NULL; |
PixMapHandle myDstPixMap = NULL; |
OSErr myErr = noErr; |
// get the current port and device |
GetGWorld(&mySavedPort, &mySavedGDevice); |
// create a video track in the movie |
*theSourceTrack = NewMovieTrack(*theMovie, IntToFixed(theWidth), IntToFixed(theHeight), kNoVolume); |
if (theSourceTrack == NULL) |
goto bail; |
myMedia = NewTrackMedia(*theSourceTrack, VideoMediaType, kVideoTrackTimeScale, NULL, 0); |
if (myMedia == NULL) |
goto bail; |
// get the rectangle for the movie |
GetMovieBox(*theMovie, &myRect); |
// begin editing the new track |
myErr = BeginMediaEdits(myMedia); |
if (myErr != noErr) |
goto bail; |
// create a new GWorld; we draw the picture into this GWorld and then compress it |
// (note that we are creating a picture with the maximum bit depth) |
myErr = NewGWorld(&myGWorld, 32, &myRect, NULL, NULL, 0L); |
if (myErr != noErr) |
goto bail; |
mySrcPixMap = GetGWorldPixMap(theGW); |
// LockPixels(mySrcPixMap); |
myDstPixMap = GetGWorldPixMap(myGWorld); |
LockPixels(myDstPixMap); |
// create a new image description; CompressImage will fill in the fields of this structure |
mySampleDesc = (ImageDescriptionHandle)NewHandle(4); |
SetGWorld(myGWorld, NULL); |
#if TARGET_OS_MAC |
GetPortBounds(theGW, &myRect2); |
GetPortBounds(myGWorld, &myRect3); |
#endif |
#if TARGET_OS_WIN32 |
myRect2 = theGW->portRect; |
myRect3 = myGWorld->portRect; |
#endif |
// copy the image from the specified GWorld into the new GWorld |
CopyBits((BitMapPtr)*mySrcPixMap, (BitMapPtr)*myDstPixMap, &myRect2, &myRect3, srcCopy, NULL); |
// restore the original port and device |
SetGWorld(mySavedPort, mySavedGDevice); |
myErr = GetMaxCompressionSize(myDstPixMap, &myRect, 0, codecNormalQuality, kJPEGCodecType, anyCodec, &mySize); |
if (myErr != noErr) |
goto bail; |
myData = NewHandle(mySize); |
if (myData == NULL) |
goto bail; |
HLockHi(myData); |
#if TARGET_CPU_68K |
myDataPtr = StripAddress(*myData); |
#else |
myDataPtr = *myData; |
#endif |
myErr = CompressImage(myDstPixMap, &myRect, codecNormalQuality, kJPEGCodecType, mySampleDesc, myDataPtr); |
if (myErr != noErr) |
goto bail; |
myErr = AddMediaSample(myMedia, myData, 0, (**mySampleDesc).dataSize, theDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, NULL); |
if (myErr != noErr) |
goto bail; |
myErr = EndMediaEdits(myMedia); |
if (myErr != noErr) |
goto bail; |
myErr = InsertMediaIntoTrack(*theSourceTrack, theStartTime, 0, GetMediaDuration(myMedia), fixed1); |
bail: |
// restore the original port and device |
SetGWorld(mySavedPort, mySavedGDevice); |
if (myData != NULL) { |
HUnlock(myData); |
DisposeHandle(myData); |
} |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
// if (mySrcPixMap != NULL) |
// UnlockPixels(mySrcPixMap); |
if (myDstPixMap != NULL) |
UnlockPixels(myDstPixMap); |
if (myGWorld != NULL) |
DisposeGWorld(myGWorld); |
return(myErr); |
} |
////////// |
// |
// EffectsUtils_GetFrontmostTrackLayer |
// Return the layer number of the frontmost track of the specified kind in a movie. |
// |
////////// |
short EffectsUtils_GetFrontmostTrackLayer (Movie theMovie, OSType theTrackType) |
{ |
short myLayer = 0; |
short myIndex = 1; |
Track myTrack = NULL; |
// get the layer number of the first track of the specified kind; |
// if no track of that kind exists in the movie, return 0 |
myTrack = GetMovieIndTrackType(theMovie, 1, theTrackType, movieTrackMediaType | movieTrackEnabledOnly); |
if (myTrack == NULL) |
return(myLayer); |
myLayer = GetTrackLayer(myTrack); |
// see if any of the remaining tracks have lower layer numbers |
while (myTrack != NULL) { |
if (myLayer > GetTrackLayer(myTrack)) |
myLayer = GetTrackLayer(myTrack); |
myIndex++; |
myTrack = GetMovieIndTrackType(theMovie, myIndex, theTrackType, movieTrackMediaType | movieTrackEnabledOnly); |
} |
return(myLayer); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14