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.
QTShowEffect.c
////////// |
// |
// File: QTShowEffect.c |
// |
// Contains: Code to generate a QuickTime movie with a QuickTime video effect in it, |
// and to preview an effect on zero, one, or two pictures. |
// |
// Written by: Tim Monroe |
// Based on existing ShowEffect code written by Dan Crow, |
// which was adapted from Peter Hoddie's original DoEffect code. |
// |
// Copyright: © 1996-1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <37> 03/19/01 rtm added MacSetPort call to QTEffects_HandleEffectsDialogEvents (so GlobalToLocal |
// would work correctly) |
// <36> 09/01/00 rtm made gChooseDialog global, so we can just hide and show it instead of rebuilding |
// it every time; this fixes a problem with RezWacked files on Windows (menu was |
// getting lost...); commented out call to QTEffects_UninitializePopUpMenu |
// <35> 06/08/00 rtm fixed crashing bug in QTEffects_SetUpEffectSequence caused by illegal cast |
// <34> 06/07/00 rtm fixed dynamic building of Effects pop-up menu, using info in Tech Note TB42; |
// added QTEffects_UninitializePopUpMenu |
// <33> 03/20/00 rtm made changes to get things running under CarbonLib |
// <32> 05/24/99 rtm fixed problem that caused image buffers to get corrupted on certain operations; |
// we were not careful in our locking and unlocking the image pixmaps, so sometimes |
// they would get unlocked and remain unlocked; now we lock the image pixmaps for |
// gGW1 and gGW2 when the GWorlds are created, and then we leave them locked forever |
// <31> 05/03/99 rtm made call to GetCCursor cross-platform again; changed name of main effects |
// display window; removed most support for compound effects; converted some |
// hard-coded strings into constants; on Windows, made main effects display window |
// a child of ghWndMDIClient (still some remaining cosmetic glitches, tho') |
// <30> 03/23/99 rtm started to add support for compound effects: renamed _CreateTwoSourceEffectDescription |
// as _CreateEffectDescription |
// <29> 03/02/99 rtm added support for MakeImageDescriptionForEffect (QT 4.0 and later) |
// <28> 02/04/99 rtm made call to GetCCursor Mac-only (crashes on Windows); filed bug report |
// <27> 09/30/98 rtm tweaked call to AddMovieResource to create single-fork movies |
// <26> 05/21/98 rtm removed conditionalized support for compound effects |
// <25> 05/20/98 rtm changed stacked calls to NewGWorld to a single QTNewGWorld call in the |
// functions QTEffects_GetPictResourceAsGWorld and QTEffects_GetPictureAsGWorld; |
// fixed crashing bug in QTEffects_HandleEffectsDialogEvents by adding the test |
// "&& (gSubPanelPopUpControl != NULL)" in control handle test |
// <24> 04/23/98 rtm changed gw->portPixMap to GetGWorldPixMap in CopyBits calls |
// <23> 03/24/98 rtm fixed minor problems with subpanels in custom dialog box |
// <22> 03/23/98 rtm added support for subpanels in custom dialog box |
// <21> 03/09/98 rtm changed kEffectSourceName constant in QTEffects_AddTrackReferenceToInputMap |
// back to kEffectDataSourceType; now "Build Movie" works again |
// <20> 03/06/98 rtm moved custom dialog item handling to before the call to |
// ImageCodecIsStandardParameterDialogEvent in QTEffects_HandleEffectsDialogEvents |
// <19> 03/02/98 rtm further clean-up on Windows |
// <18> 02/26/98 rtm added QTEffects_HandleEffectsDialogEvents, QTEffects_EffectsDialogCallback, |
// and QTEffects_CustomDialogWndProc to properly handle Windows dialog boxes; |
// miscellaneous other clean-up; added QTUtils_SetMovieFileLoopingInfo to |
// QTEffects_CreateEffectsMovie |
// <17> 02/21/98 rtm added palindrome looping and stepping backward |
// <16> 02/20/98 rtm revised custom dialog box handling; now works on Windows (yippee!) |
// <15> 02/18/98 rtm removed support for thumbnail movies |
// <14> 02/17/98 rtm conditionalized support for compound effects (not yet implemented by QT 3.0) |
// <13> 02/16/98 rtm added QTEffects_HandleEffectsWindowEvents and *Messages for platform-specific |
// event/message handling for main effects window |
// <12> 02/13/98 rtm reworked QTEffects_DrawEffectsWindow to support stepping through an effect |
// <11> 02/12/98 rtm removed all support for Preferences dialog box (Settings menu is enough) |
// <10> 02/11/98 rtm reworked QTEffects_CreateTwoSourceEffectDescription to return an effect |
// description instead of setting gCurrentState.fEffectDescription directly; |
// began adding support for compound effects |
// <9> 02/10/98 rtm added call to WriteResource to QTEffects_AddListOfEffects |
// <8> 02/07/98 rtm rewrote QTEffects_AddListOfEffects to use QTGetEffectsList |
// <7> 02/04/98 rtm fixed problems with custom dialog boxes; see the discussion in the |
// function QTEffects_LetUserCustomizeEffect for details |
// <6> 02/03/98 rtm modified QTEffects_DrawEffectsWindow and QTEffects_AddVideoTrackFromGWorld |
// to copy image(s) from existing GWorld(s); added ability to read pictures |
// from files (in addition to resources) using graphics importer routines |
// <5> 01/31/98 rtm added Settings menu to replace Preferences dialog box |
// <4> 01/30/98 rtm fixed video track durations; now everything works fine on MacOS |
// <3> 12/19/97 rtm added gModalFilterUPP to ModalDialog so that the Select Effect |
// dialog box can be moved |
// <2> 12/18/97 rtm continued clean-up: removed unused global variables and added |
// constants for dialog box item indices |
// <1> 12/15/97 rtm first file; integrated existing code with shell framework |
// |
// This file defines functions that display a QuickTime video effect as a transition from one picture |
// to another, or a QuickTime video effect applied to a single picture. The user selects the effect |
// using a simple dialog box with a pop-up menu; then the user selects effect parameters using either |
// the standard effects parameter dialog box or a custom effects parameter dialog box. Once an effect |
// and some parameters are selected, the user can: |
// |
// * run the effect once through from beginning to end |
// * run the effect continuously in a normal or palindrome loop |
// * run a single step of the effect, either forward or backward |
// * generate a QuickTime movie file containing the current effect |
// * select the picture(s) to which the effect is applied |
// |
// Here we use the low-level QuickTime video effects APIs, for greater control over the process. The low-level |
// API includes functions beginning "ImageCodec", for example: ImageCodecCreateStandardParameterDialog. |
// The main advantage to using the low-level calls is the ability to embed the effect parameter dialog items |
// into a custom, application-specific dialog box. (We do however use the high-level function QTGetEffectsList |
// when building a list of the available effects.) |
// |
// NOTE: |
// If you set the compiler flag ALLOW_COMPOUND_EFFECTS to 1, then the Build Effect Movie menu item will add |
// the film noise filter to whatever two-source effect you've already chosen when it builds the movie; this is |
// intended to illustrate how to work with compound (or "stacked") effects. QTShowEffect does not yet provide |
// a mechanism to allow the user to add a compound effect to the one shown in the main effects display window. |
// |
////////// |
////////// |
// |
// TO DO: |
// + |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "QTShowEffect.h" |
////////// |
// |
// global variables |
// |
////////// |
WindowPtr gMainWindow = NULL; // the main effect-display window |
QTParameterDialog gEffectsDialog = 0L; // identifier for the standard parameter dialog box |
DialogPtr gCustomDialog = NULL; // the dialog that incorporates the standard parameter dialog box user interface elements |
DialogPtr gChooseDialog = NULL; // the dialog for choosing an effect |
GWorldPtr gGW1 = NULL; // the GWorlds that hold the effect sources |
GWorldPtr gGW2 = NULL; |
ImageDescriptionHandle gGW1Desc = NULL; // the image descriptions of the GWorlds |
ImageDescriptionHandle gGW2Desc = NULL; |
ComponentInstance gCompInstance = NULL; // the instance of the current effect component |
unsigned short gLoopingState = kNormalLooping; // the current looping state of effect display |
unsigned short gCurrentDir = kForward; // the current direction of effect display |
Boolean gUseStandardDialog = true; // if true, use the standard effect parameter dialog box; if false, use a custom effect parameter dialog box |
Boolean gFastEffectDisplay = false; // if true, the effect is run to completion immediately; |
// if false, the effect runs as tickled by the event loop or message stream |
PopUpMenuInformation gSelectEffectPopup; // holds information about the Select Effect popup menu |
StateInformation gCurrentState; // holds information about the current state of effects processing |
int gNumberOfSteps = k30StepsCount; |
MenuHandle gSubPanelPopUpMenu = NULL; // menu handle for subpanel pop-up menu in custom dialog box |
ControlHandle gSubPanelPopUpControl = NULL; // control handle for subpanel pop-up menu in custom dialog box |
extern ModalFilterUPP gModalFilterUPP; |
#if TARGET_OS_WIN32 |
char gEffectsWindowClassName[] = kEffectsWindowClassName; |
extern HANDLE ghInst; // the instance of this application |
extern HWND ghWndMDIClient; // the MDI client window |
extern Boolean gShuttingDown; |
#endif |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Application start-up and shut-down functions. |
// |
// Use these functions to initialize and tear down effects processing for this application. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_Init |
// Do application-specific initialization for effects processing. |
// |
////////// |
OSErr QTEffects_Init (void) |
{ |
CCrsrHandle myCursor = NULL; |
OSErr myErr = noErr; |
#if TARGET_OS_WIN32 |
HWND myWindow; |
#endif |
#if TARGET_OS_MAC |
StringPtr myString; |
Rect myRect; |
DialogPtr myDialog = NULL; |
// show a splash screen while we set up the default values and other such things |
myDialog = GetNewDialog(kSplashDialogID, NULL, (WindowPtr)-1); |
if (myDialog != NULL) { |
MacSetPort(GetDialogPort(myDialog)); |
MacShowWindow(GetDialogWindow(myDialog)); |
DrawDialog(myDialog); |
} |
#endif |
// get wristwatch cursor; this initialization might take a little while.... |
myCursor = GetCCursor(kWatchCursorResID); |
if (myCursor != NULL) |
SetCCursor(myCursor); |
// set up the initial state |
gCurrentState.fSampleDescription = NULL; |
gCurrentState.fEffectDescription = NULL; |
gCurrentState.fEffectType = kCrossFadeTransitionType; |
gCurrentState.fShowingEffect = false; |
gCurrentState.fTime = 0; |
gCurrentState.fEffectSequenceID = 0L; |
gCurrentState.fTimeBase = NULL; |
// create the pop-up menu for the Select Effect dialog box |
myErr = QTEffects_InitializePopUpMenu(&gSelectEffectPopup); |
if (myErr != noErr) |
goto bail; |
// add items to the menu |
myErr = QTEffects_AddListOfEffects(); |
if (myErr != noErr) { |
#if TARGET_OS_MAC |
QTFrame_ShowWarning("\pCannot run this application from a locked volume; please copy to a different disk. Quitting.", 0); |
ExitToShell(); |
#endif |
#if TARGET_OS_WIN32 |
PostQuitMessage(0); |
#endif |
} |
// create the GWorlds containing the pictures that will act as the sources for the effect to be displayed; |
// on application start-up, we'll use canned pictures in resources |
myErr = QTEffects_GetPictResourceAsGWorld(kFirstPICTResID, kWidth, kHeight, kDepth, &gGW1); |
if (myErr != noErr) |
goto bail; |
myErr = QTEffects_GetPictResourceAsGWorld(kSecondPICTResID, kWidth, kHeight, kDepth, &gGW2); |
if (myErr != noErr) |
goto bail; |
// lock the pixmaps; henceforth, whenever we draw into or copy out of these GWorlds, we shall assume that |
// the pixmaps have previously been locked |
if (!LockPixels(GetGWorldPixMap(gGW1))) |
goto bail; |
if (!LockPixels(GetGWorldPixMap(gGW2))) |
goto bail; |
// ***create the main effects display window*** |
#if TARGET_OS_MAC |
// on MacOS, we create a standard Macintosh window and keep track of it in the global variable gMainWindow |
myString = QTUtils_ConvertCToPascalString(kEffectsWindowTitle); |
MacSetRect(&myRect, kWindowOffset, kWindowOffset, kWindowOffset + kWidth, kWindowOffset + kHeight); |
gMainWindow = NewCWindow(NULL, &myRect, myString, true, 0, (WindowPtr)-1, true, 0); |
free(myString); |
#endif |
#if TARGET_OS_WIN32 |
// on Windows, we create an instance of a custom window class (gEffectsWindowClassName), associate a port |
// with it, and then keep track of that port in the global variable gMainWindow |
QTEffects_RegisterEffectsWindowClass(ghInst); |
myWindow = CreateWindow(gEffectsWindowClassName, kEffectsWindowTitle, |
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_CHILD | WS_SYSMENU, |
kWindowOffset, |
kWindowOffset, |
kWidth + (2 * GetSystemMetrics(SM_CXBORDER)), |
kHeight + (2 * GetSystemMetrics(SM_CXBORDER)) + GetSystemMetrics(SM_CYCAPTION) - 1, |
ghWndMDIClient, |
NULL, |
ghInst, |
NULL); |
if (myWindow != NULL) |
gMainWindow = (WindowPtr)CreatePortAssociation(myWindow, NULL, 0L); |
#endif |
bail: |
#if TARGET_OS_MAC |
// close down the splash screen |
if (myDialog != NULL) |
DisposeDialog(myDialog); |
#endif |
// restore the cursor to the arrow |
InitCursor(); |
if (myCursor != NULL) |
DisposeCCursor(myCursor); |
// now prompt the user for the first effect |
if ((gMainWindow != NULL) && (myErr == noErr)) |
QTApp_HandleMenu(IDM_SELECT_EFFECT); |
return(myErr); |
} |
////////// |
// |
// QTEffects_Stop |
// Do application-specific deinitialization for effects processing. |
// |
////////// |
OSErr QTEffects_Stop (void) |
{ |
OSErr myErr = noErr; |
// close the main effects window |
#if TARGET_OS_WIN32 |
if (gMainWindow != NULL) |
DestroyWindow(QTFrame_GetWindowReferenceFromPort(gMainWindow)); |
#endif |
#if TARGET_OS_MAC |
if (gMainWindow != NULL) |
DisposeWindow(gMainWindow); |
#endif |
if (gChooseDialog != NULL) |
DisposeDialog(gChooseDialog); |
// unhook the effects pop-up menu from the menu list and dispose of it |
// QTEffects_UninitializePopUpMenu(&gSelectEffectPopup); |
// deallocate any global storage |
if (gGW1Desc != NULL) |
DisposeHandle((Handle)gGW1Desc); |
if (gGW2Desc != NULL) |
DisposeHandle((Handle)gGW2Desc); |
if (gGW1 != NULL) |
DisposeGWorld(gGW1); |
if (gGW2 != NULL) |
DisposeGWorld(gGW2); |
if (gCurrentState.fSampleDescription != NULL) |
DisposeHandle((Handle)gCurrentState.fSampleDescription); |
if (gCurrentState.fEffectDescription != NULL) |
QTDisposeAtomContainer(gCurrentState.fEffectDescription); |
if (gCurrentState.fEffectSequenceID != 0L) |
CDSequenceEnd(gCurrentState.fEffectSequenceID); |
if (gCurrentState.fTimeBase != NULL) |
DisposeTimeBase(gCurrentState.fTimeBase); |
if (gCompInstance != NULL) |
CloseComponent(gCompInstance); |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Effects window functions. |
// |
// Use these functions to set up and manage the main effects window displayed by this application. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_ProcessEffect |
// Play one or more steps of the current effect. |
// |
////////// |
void QTEffects_ProcessEffect (void) |
{ |
OSErr myErr = noErr; |
// if we are in "fast mode", play the effect forward thru to completion |
if (gCurrentState.fShowingEffect && gFastEffectDisplay) { |
for (gCurrentState.fTime = 1; gCurrentState.fTime <= gNumberOfSteps; gCurrentState.fTime++) { |
myErr = QTEffects_RunEffect(gCurrentState.fTime); |
if (myErr != noErr) |
return; |
} |
gCurrentState.fShowingEffect = false; |
} |
// otherwise, show the next step of the effect |
if (gCurrentState.fShowingEffect && !gFastEffectDisplay) { |
if (gCurrentDir == kForward) { |
gCurrentState.fTime++; |
if (gCurrentState.fTime > gNumberOfSteps) { |
switch (gLoopingState) { |
case kNoLooping: |
gCurrentState.fTime = 0; |
gCurrentDir = kForward; |
gCurrentState.fShowingEffect = false; |
break; |
case kNormalLooping: |
gCurrentState.fTime = 0; |
gCurrentDir = kForward; |
gCurrentState.fShowingEffect = true; |
break; |
case kPalindromeLooping: |
gCurrentState.fTime = gNumberOfSteps; |
gCurrentDir = kBackward; |
gCurrentState.fShowingEffect = true; |
break; |
} |
} |
} else { |
gCurrentState.fTime--; |
if (gCurrentState.fTime < 1) { |
switch (gLoopingState) { |
case kNoLooping: |
gCurrentState.fTime = 0; |
gCurrentDir = kForward; |
gCurrentState.fShowingEffect = false; |
break; |
case kPalindromeLooping: |
case kNormalLooping: // (this should never actually happen, since we're going backward) |
gCurrentState.fTime = 0; |
gCurrentDir = kForward; |
gCurrentState.fShowingEffect = true; |
break; |
} |
} |
} |
// run the next step of the effect |
myErr = QTEffects_RunEffect(gCurrentState.fTime); |
if (myErr != noErr) |
return; |
if (gCurrentState.fSteppingEffect) |
gCurrentState.fShowingEffect = false; |
} |
} |
////////// |
// |
// QTEffects_DrawEffectsWindow |
// Draw the contents of the main effects window. |
// |
////////// |
void QTEffects_DrawEffectsWindow (void) |
{ |
if ((gCurrentState.fEffectSequenceID != 0L) && (gCurrentState.fTime != 1)) { |
QTEffects_RunEffect(gCurrentState.fTime); |
} else { |
// if we haven't set up an effect yet (which presumably happens only when the application is starting up) |
// or if we're at the first frame, just copy the first source image into the window |
CGrafPtr mySavedPort = NULL; |
GDHandle mySavedGDevice = NULL; |
Rect myRectGW1; |
Rect myRectMain; |
// get the current port and device |
GetGWorld(&mySavedPort, &mySavedGDevice); |
#if TARGET_OS_MAC |
MacSetPort((GrafPtr)GetWindowPort(gMainWindow)); |
GetPortBounds(gGW1, &myRectGW1); |
GetPortBounds(GetWindowPort(gMainWindow), &myRectMain); |
#endif |
#if TARGET_OS_WIN32 |
MacSetPort((GrafPtr)gMainWindow); |
myRectGW1 = gGW1->portRect; |
myRectMain = gMainWindow->portRect; |
#endif |
// copy the image from the first source GWorld into the main window |
CopyBits( (BitMapPtr)*GetGWorldPixMap(gGW1), |
(BitMapPtr)*GetGWorldPixMap(GetWindowPort(gMainWindow)), |
&myRectGW1, |
&myRectMain, |
srcCopy, |
NULL); |
// restore the original port and device |
SetGWorld(mySavedPort, mySavedGDevice); |
} |
} |
#if TARGET_OS_MAC |
////////// |
// |
// QTEffects_HandleEffectsWindowEvents |
// Handle Macintosh events for the main effects window. |
// |
////////// |
Boolean QTEffects_HandleEffectsWindowEvents (EventRecord *theEvent) |
{ |
Boolean isHandled = false; |
short myWindowPart; |
WindowPtr myWindow; |
Rect myRect; |
switch (theEvent->what) { |
case mouseDown: |
myWindowPart = MacFindWindow(theEvent->where, &myWindow); |
if (myWindow != gMainWindow) |
break; |
switch (myWindowPart) { |
case inDrag: |
GetRegionBounds(GetGrayRgn(), &myRect); |
DragWindow(myWindow, theEvent->where, &myRect); |
isHandled = true; |
break; |
case inContent: |
SelectWindow(myWindow); |
MacSetPort(GetWindowPort(myWindow)); |
QTApp_HandleContentClick(myWindow, theEvent); |
isHandled = true; |
break; |
case inGoAway: |
// if the close box on the main window is clicked, dispose the window and quit the application |
if (TrackGoAway(myWindow, theEvent->where)) |
if (myWindow == gMainWindow) { |
DisposeWindow(myWindow); |
gMainWindow = NULL; |
QTFrame_QuitFramework(); |
} |
isHandled = true; |
break; |
} // end switch(myWindowPart) |
break; |
case updateEvt: |
myWindow = (WindowPtr)theEvent->message; |
if (myWindow == gMainWindow) { |
QTApp_Draw(myWindow); |
isHandled = true; |
} |
break; |
default: |
break; |
} |
return(isHandled); |
} |
#endif // TARGET_OS_MAC |
#if TARGET_OS_WIN32 |
////////// |
// |
// QTEffects_HandleEffectsWindowMessages |
// Handle Windows messages for the main effects window. |
// |
////////// |
LRESULT CALLBACK QTEffects_HandleEffectsWindowMessages (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam) |
{ |
PAINTSTRUCT myPaintStruct; |
// run the next step(s) of the effect |
if (!gShuttingDown) |
QTEffects_ProcessEffect(); |
switch (theMessage) { |
case WM_PAINT: |
BeginPaint(theWnd, &myPaintStruct); |
QTEffects_DrawEffectsWindow(); |
EndPaint(theWnd, &myPaintStruct); |
break; |
case WM_NCLBUTTONDOWN: |
case WM_NCRBUTTONDOWN: |
SetForegroundWindow(theWnd); |
break; |
case WM_LBUTTONDOWN: |
case WM_RBUTTONDOWN: |
SetForegroundWindow(theWnd); |
QTApp_HandleContentClick(theWnd, NULL); |
return(0); |
case WM_CLOSE: |
if (theWnd != NULL) |
DestroyWindow(theWnd); |
gMainWindow = NULL; |
QTFrame_QuitFramework(); |
break; |
case WM_DESTROY: |
DestroyPortAssociation((CGrafPtr)gMainWindow); |
break; |
default: |
break; |
} |
return(DefWindowProc(theWnd, theMessage, wParam, lParam)); |
} |
////////// |
// |
// QTEffects_RegisterEffectsWindowClass |
// Register the window procedure for the main effects window. |
// |
////////// |
BOOL QTEffects_RegisterEffectsWindowClass (HANDLE hInstance) |
{ |
WNDCLASSEX myWC; |
// register the main effect window class |
myWC.cbSize = sizeof(WNDCLASSEX); |
myWC.style = CS_HREDRAW | CS_VREDRAW; |
myWC.lpfnWndProc = (WNDPROC)QTEffects_HandleEffectsWindowMessages; |
myWC.cbClsExtra = 0; |
myWC.cbWndExtra = 0; |
myWC.hInstance = hInstance; |
myWC.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPICON)); |
myWC.hCursor = LoadCursor(NULL, IDC_ARROW); |
myWC.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); |
myWC.lpszMenuName = NULL; |
myWC.lpszClassName = gEffectsWindowClassName; |
myWC.hIconSm = LoadImage(hInstance, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 16, 16, 0); |
if (!RegisterClassEx(&myWC)) { |
if (!RegisterClass((LPWNDCLASS)&myWC.style)) |
return(false); |
} |
return(true); |
} |
#endif // TARGET_OS_WIN32 |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Dialog utilities. |
// |
// Use these functions to set up and manage the dialog boxes displayed by this application. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_LetUserChooseEffect |
// Display a dialog box that contains a pop-up menu of all the effects currently available, |
// and allow the user to select an effect. |
// |
////////// |
OSErr QTEffects_LetUserChooseEffect (void) |
{ |
GrafPtr mySavedPort = NULL; |
DialogPtr myDialog = NULL; |
short myItem; |
short myType; |
Handle myItemHandle = NULL; |
Rect myRect; |
int myIndex; |
OSErr myErr = noErr; |
GetPort(&mySavedPort); |
if (gChooseDialog == NULL) { |
// get the dialog that lets the user select an effect component |
gChooseDialog = GetNewDialog(kSelectDialogID, NULL, (WindowPtr)-1); |
if (gChooseDialog == NULL) |
goto bail; |
} else { |
#if TARGET_API_MAC_CARBON |
MacShowWindow(GetDialogWindow(gChooseDialog)); |
#else |
MacShowWindow(gChooseDialog); |
#endif |
} |
myErr = SetDialogDefaultItem(gChooseDialog, kSelectButtonOK); |
if (myErr != noErr) |
goto bail; |
// find the currently selected effect in the list, so that it's showing when the dialog is displayed |
for (myIndex = 0; myIndex < gSelectEffectPopup.fNumberOfItems; myIndex++) { |
if (gSelectEffectPopup.fItemInfo[myIndex] == gCurrentState.fEffectType) { |
GetDialogItem(gChooseDialog, kSelectPopUpID, &myType, &myItemHandle, &myRect); |
SetControlValue((ControlHandle)myItemHandle, myIndex + 1); |
} |
} |
// now show the dialog |
MacShowWindow(GetDialogWindow(gChooseDialog)); |
MacSetPort(GetDialogPort(gChooseDialog)); |
do { |
#if TARGET_OS_MAC |
ModalDialog(gModalFilterUPP, &myItem); |
#endif |
#if TARGET_OS_WIN32 |
ModalDialog(NULL, &myItem); |
#endif |
} while (myItem != kSelectButtonOK); |
// get the control handle for the pop-up menu and read its current value |
GetDialogItem(gChooseDialog, kSelectPopUpID, &myType, &myItemHandle, &myRect); |
gSelectEffectPopup.fLastChosen = GetControlValue((ControlHandle)myItemHandle); |
// now the user has hit OK and gSelectEffectPopup.fLastChosen contains the item chosen. |
gCurrentState.fEffectType = (OSType)gSelectEffectPopup.fItemInfo[gSelectEffectPopup.fLastChosen - 1]; |
bail: |
if (gChooseDialog != NULL) |
#if TARGET_API_MAC_CARBON |
HideWindow(GetDialogWindow(gChooseDialog)); |
#else |
HideWindow(gChooseDialog); |
#endif |
MacSetPort(mySavedPort); |
return(myErr); |
} |
////////// |
// |
// QTEffects_LetUserCustomizeEffect |
// Display a dialog box that allows the user to select non-default parameters for an effect. |
// |
////////// |
OSErr QTEffects_LetUserCustomizeEffect (QTAtomContainer theEffectDesc) |
{ |
GrafPtr mySavedPort = NULL; |
ComponentDescription myCD; |
Component myComponent = NULL; |
QTAtomContainer myParamDesc = NULL; |
MenuHandle myMenu = NULL; |
OSErr myErr = noErr; |
GetPort(&mySavedPort); |
// set up a component description |
myCD.componentType = decompressorComponentType; // effects are image decompressor components |
myCD.componentSubType = gCurrentState.fEffectType; // whichever subtype of effect we are looking for |
myCD.componentManufacturer = 0; |
myCD.componentFlags = 0; |
myCD.componentFlagsMask = 0; |
// if the component has been previously opened, close it (since we may be customizing a different effect) |
if (gCompInstance != NULL) { |
CloseComponent(gCompInstance); |
gCompInstance = NULL; |
} |
// find the required component |
myComponent = FindNextComponent(myComponent, &myCD); |
if (myComponent == NULL) |
return(paramErr); |
// open the component |
gCompInstance = OpenComponent(myComponent); |
// get the list of parameters for the effect |
myErr = ImageCodecGetParameterList(gCompInstance, &myParamDesc); |
if (myErr != noErr) |
goto bail; |
// if the user has chosen the standard dialog box, then display it... |
if (gUseStandardDialog) { |
// set up the dialog box |
myErr = ImageCodecCreateStandardParameterDialog( |
gCompInstance, |
myParamDesc, |
theEffectDesc, |
0, // dialog options |
NULL, // no existing dialog |
0, // no existing user item |
&gEffectsDialog); |
if ((myErr != noErr) || (gEffectsDialog == 0L)) |
goto bail; |
gCustomDialog = NULL; |
} else { |
// ...otherwise, create the dialog box as a part of a custom application-supplied dialog box |
// Get the application-supplied dialog box; a few words of explanation are appropriate here: |
// QuickTime expects this dialog box to be a color window, or the slider elements (and some |
// other controls) will not draw correctly; accordingly, your application needs to include |
// several resources that have the same ID as the dialog box resource (here, kCustomDialogID). |
// For MacOS 8 and/or Appearance Manager compatibility, the application must also a resource |
// of type 'dlgx', whose data (a long word) has at least the bits kDialogFlagsUseThemeBackground |
// and kDialogFlagsUseThemeControls set; we'll also set the bit kDialogFlagsHandleMovableModal. |
// So our entire 6-byte 'dlgx' resource is 00000000000D. (The first 2 bytes of the 'dlgx' resource |
// are a version number, which we set to 0.) For vanilla System 7 compatibility, the application |
// must include a resource of type 'dctb' whose ID is kCustomDialogID. |
gCustomDialog = GetNewDialog(kCustomDialogID, NULL, (WindowPtr)-1); |
if (gCustomDialog != NULL) { |
// set up the dialog box |
myErr = ImageCodecCreateStandardParameterDialog( |
gCompInstance, |
myParamDesc, |
theEffectDesc, |
0, // dialog options |
gCustomDialog, // the existing dialog |
kCustomUserItemID, // the existing user item |
&gEffectsDialog); |
if ((myErr != noErr) || (gEffectsDialog == 0L)) |
goto bail; |
SetDialogDefaultItem(gCustomDialog, kCustomButtonOK); |
#if TARGET_OS_WIN32 |
// on Windows, we need to explicitly tag the dialog box as a movable modal dialog box |
SetDialogMovableModal(gCustomDialog); |
#endif |
// see whether the current effect supports subpanels; |
// if so, display a pop-up menu that contains the names of those subpanels |
myErr = ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionGetSubPanelMenu, &myMenu); |
if ((myErr == noErr) && (myMenu != NULL)) { |
short myNumItems; |
short myIndex; |
short myItemKind; |
Handle myItemHandle = NULL; |
Rect myItemRect; |
// remove any existing menu items in gSubPanelPopUpMenu |
if (gSubPanelPopUpMenu != NULL) { |
myNumItems = CountMenuItems(gSubPanelPopUpMenu); |
for (myIndex = 0; myIndex < myNumItems; myIndex++) |
DeleteMenuItem(gSubPanelPopUpMenu, 1); |
} |
// get the control handle of the pop-up menu |
GetDialogItem(gCustomDialog, kCustomPopUpID, &myItemKind, &myItemHandle, &myItemRect); |
gSubPanelPopUpControl = (ControlHandle)myItemHandle; |
// get the menu handle of the pop-up menu |
#if TARGET_API_MAC_CARBON |
gSubPanelPopUpMenu = GetControlPopupMenuHandle(gSubPanelPopUpControl); |
#else |
gSubPanelPopUpMenu = (**(PopupPrivateDataHandle)(**gSubPanelPopUpControl).contrlData).mHandle; |
#endif |
myNumItems = CountMenuItems(myMenu); |
for (myIndex = 1; myIndex <= myNumItems; myIndex++) { |
Str255 myString; |
// copy items into the pop-up menu |
GetMenuItemText(myMenu, myIndex, myString); |
MacInsertMenuItem(gSubPanelPopUpMenu, myString, myIndex + 1); |
} |
// set the minimum, maximum, and initial values of the pop-up menu |
SetControlMinimum(gSubPanelPopUpControl, 1); |
SetControlMaximum(gSubPanelPopUpControl, myNumItems); |
SetControlValue(gSubPanelPopUpControl, 1); |
ShowControl(gSubPanelPopUpControl); |
} |
MacShowWindow(GetDialogWindow(gCustomDialog)); |
MacSetPort(GetDialogPort(gCustomDialog)); |
} |
} |
// now, the frontmost window is a standard or custom effects parameter dialog box; |
// on the Mac, we call QTEffects_HandleEffectsDialogEvents in our main event loop |
// to find and process events targeted at the effects parameter dialog box; on Windows, |
// we need to use a different strategy: we install a modeless dialog callback procedure |
// that is called internally by QTML |
#if TARGET_OS_WIN32 |
SetModelessDialogCallbackProc(FrontWindow(), (QTModelessCallbackUPP)QTEffects_EffectsDialogCallback); |
QTMLSetWindowWndProc(FrontWindow(), QTEffects_CustomDialogWndProc); |
#endif |
bail: |
if (myParamDesc != NULL) |
QTDisposeAtomContainer(myParamDesc); |
MacSetPort(mySavedPort); |
return(myErr); |
} |
#if TARGET_OS_WIN32 |
////////// |
// |
// QTEffects_EffectsDialogCallback |
// This function is called by QTML when it processes events for the standard or custom effects parameter dialog box. |
// |
////////// |
static void QTEffects_EffectsDialogCallback (EventRecord *theEvent, DialogRef theDialog, DialogItemIndex theItemHit) |
{ |
QTParamDialogEventRecord myRecord; |
myRecord.theEvent = theEvent; |
myRecord.whichDialog = theDialog; |
myRecord.itemHit = theItemHit; |
if (gEffectsDialog != 0L) { |
ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionModelessCallback, &myRecord); |
// see if the event is meant for the effects parameter dialog box |
QTEffects_HandleEffectsDialogEvents(theEvent, theItemHit); |
} |
} |
////////// |
// |
// QTEffects_CustomDialogWndProc |
// Handle messages for the custom effects parameters dialog box. |
// |
////////// |
LRESULT CALLBACK QTEffects_CustomDialogWndProc (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam) |
{ |
EventRecord myEvent = {0}; |
// pass idle events thru to the dialog callback |
if (theMessage == 0x7FFF) |
QTEffects_EffectsDialogCallback(&myEvent, GetNativeWindowPort(theWnd), 0); |
return(DefWindowProc(theWnd, theMessage, wParam, lParam)); |
} |
#endif |
////////// |
// |
// QTEffects_HandleEffectsDialogEvents |
// Process events that might be targeted at the standard or custom effects parameter dialog box. |
// Return true if the event was completely handled. |
// |
////////// |
Boolean QTEffects_HandleEffectsDialogEvents (EventRecord *theEvent, DialogItemIndex theItemHit) |
{ |
#if TARGET_OS_MAC |
#pragma unused(theItemHit) |
#endif |
Boolean isHandled = false; |
Boolean myAcceptChanges = false; |
Boolean myCloseDialog = false; |
OSErr myErr = noErr; |
// handle events for our own items in the custom dialog box, if it exists; |
// we need to do this BEFORE we pass the event to ImageCodecIsStandardParameterDialogEvent |
if (gCustomDialog != NULL) { |
#if TARGET_OS_WIN32 |
// on Windows, QTML has already done any required control tracking for us, and |
// the theItemHit parameter contains the dialog item number of the selected item |
switch (theItemHit) { |
case kCustomPopUpID: |
ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionActivateSubPanel, (void *)GetControlValue(gSubPanelPopUpControl)); |
break; |
case kCustomButtonOK: |
myAcceptChanges = true; |
myCloseDialog = true; |
break; |
default: |
break; |
} |
#endif |
#if TARGET_OS_MAC |
// on the Mac, we need to do our own events processing |
switch (theEvent->what) { |
case mouseDown: { |
WindowPtr myWindow; |
short myPart; |
ControlHandle myControl; |
Point myPoint; |
myPart = MacFindWindow(theEvent->where, &myWindow); |
if ((myWindow == GetDialogWindow(gCustomDialog)) && (myPart == inContent)) { |
// make sure we are the current drawing port (else GlobalToLocal won't work correctly) |
MacSetPort(GetDialogPort(gCustomDialog)); |
myPoint = theEvent->where; |
GlobalToLocal(&myPoint); |
myPart = FindControl(myPoint, myWindow, &myControl); |
// first, see if the user clicked in the subpanel pop-up menu |
if ((myControl == gSubPanelPopUpControl) && (gSubPanelPopUpControl != NULL)) { |
TrackControl(myControl, myPoint, (ControlActionUPP)-1); |
ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionActivateSubPanel, (void *)GetControlValue(myControl)); |
return(true); |
} |
// next, see if the user clicked the OK button |
if (myPart == kControlButtonPart) { |
if (TrackControl(myControl, myPoint, NULL) != 0) { |
myAcceptChanges = true; |
myCloseDialog = true; |
} |
} |
// handle any other custom dialog box items here |
} |
break; |
} |
case keyDown: |
case autoKey: |
// handle the standard keyboard equivalents of OK button |
switch (theEvent->message & charCodeMask) { |
case kReturnKey: |
case kEnterKey: |
myAcceptChanges = true; |
myCloseDialog = true; |
break; |
default: |
break; |
} |
break; |
} |
#endif |
} // if (gCustomDialog != NULL) |
// pass the event to the standard effects parameter dialog box handler |
myErr = ImageCodecIsStandardParameterDialogEvent(gCompInstance, theEvent, gEffectsDialog); |
switch (myErr) { |
case codecParameterDialogConfirm: |
// the user has selected the OK button of the standard (not custom) effects parameter dialog box |
myAcceptChanges = true; |
myCloseDialog = true; |
break; |
case userCanceledErr: |
// the user has selected the Cancel button of the standard (not custom) effects parameter dialog box |
myCloseDialog = true; |
break; |
case featureUnsupported: |
// the event was *not* handled by ImageCodecIsStandardParameterDialogEvent; |
// let the event be processed normally |
break; |
case noErr: |
default: |
// the event was completely handled by ImageCodecIsStandardParameterDialogEvent |
isHandled = true; |
break; |
} |
if (myCloseDialog) { |
// retrieve the values from the parameters dialog box |
if (myAcceptChanges) |
ImageCodecStandardParameterDialogDoAction(gCompInstance, gEffectsDialog, pdActionConfirmDialog, NULL); |
// remove the dialog box |
ImageCodecDismissStandardParameterDialog(gCompInstance, gEffectsDialog); |
if (gCustomDialog != NULL) |
DisposeDialog(gCustomDialog); |
gCustomDialog = NULL; |
gEffectsDialog = 0L; |
isHandled = true; |
// now prepare the decompression sequence |
myErr = QTEffects_SetUpEffectSequence(); |
} |
return(isHandled); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Pop-up menu utilities. |
// |
// Use these functions to set up and manage the global pop-up menu of effects used by this application. |
// Here's our strategy: in our original resource file, the menu resource with ID kPopUpMenuID is empty; |
// at run-time, we open the menu resource, remove any items from it, and then add a list of all effects |
// to it. Then we write the new menu resource out to the resource file, whence GetNewDialog can find it. |
// Very clever. |
// |
// Yeah, really clever...and not at all Carbonizable. Why? Apparently under Carbon, menu handles read from |
// resources are not treated as resource handles. So calling ChangedResource and WriteResource no longer |
// works. A better way to handle this is to follow the advice of Tech Note TB42 and insert the new menu |
// into the menu list, whence the pop-up menu CDEF will retrieve it when GetNewDialog is called. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_InitializePopUpMenu |
// Initialize the global pop-up effects menu. |
// |
////////// |
OSErr QTEffects_InitializePopUpMenu (PopUpMenuInformation *theMenuInfo) |
{ |
short myIndex; |
short myNumItems; |
OSErr myErr = noErr; |
theMenuInfo->fMenu = MacGetMenu(kPopUpMenuID); |
if (theMenuInfo->fMenu == NULL) { |
myErr = ResError(); |
goto bail; |
} |
// make sure the menu isn't purgeable |
HNoPurge((Handle)theMenuInfo->fMenu); |
// remove any existing menu items (since we're going to generate the effects list dynamically) |
myNumItems = CountMenuItems(theMenuInfo->fMenu); |
for (myIndex = 0; myIndex < myNumItems; myIndex++) |
DeleteMenuItem(theMenuInfo->fMenu, 1); |
theMenuInfo->fNumberOfItems = 0; |
theMenuInfo->fLastChosen = 1; |
// insert the menu into the menu list; the pop-up menu CDEF will find it there |
// when we open the dialog box containing this pop-up menu |
MacInsertMenu(theMenuInfo->fMenu, kInsertHierarchicalMenu); |
bail: |
return(myErr); |
} |
////////// |
// |
// QTEffects_UninitializePopUpMenu |
// Uninitialize the global pop-up effects menu. |
// |
////////// |
OSErr QTEffects_UninitializePopUpMenu (PopUpMenuInformation *theMenuInfo) |
{ |
#if ACCESSOR_CALLS_ARE_FUNCTIONS |
MacDeleteMenu(GetMenuID(theMenuInfo->fMenu)); |
#else |
MacDeleteMenu((**theMenuInfo->fMenu).menuID); |
#endif |
DisposeMenu(theMenuInfo->fMenu); |
return(noErr); |
} |
////////// |
// |
// QTEffects_AddItemToPopUpMenu |
// Add an item to the global pop-up effects menu. |
// |
////////// |
OSErr QTEffects_AddItemToPopUpMenu (PopUpMenuInformation *theMenuInfo, char *theItemText, OSType theItemInfo) |
{ |
char myCopy[256]; |
StringPtr myString = NULL; |
OSErr myErr = noErr; |
strcpy(myCopy, theItemText); |
strcpy(theMenuInfo->fMenuText[theMenuInfo->fNumberOfItems], myCopy); |
myString = QTUtils_ConvertCToPascalString(myCopy); |
MacInsertMenuItem(theMenuInfo->fMenu, (unsigned char *)myString, theMenuInfo->fNumberOfItems); |
theMenuInfo->fItemInfo[theMenuInfo->fNumberOfItems] = theItemInfo; |
theMenuInfo->fNumberOfItems++; |
free(myString); |
return(myErr); |
} |
////////// |
// |
// QTEffects_AddListOfEffects |
// Add a list of the available effects to the global pop-up effects menu. |
// |
////////// |
OSErr QTEffects_AddListOfEffects (void) |
{ |
QTAtomContainer myEffectsList = NULL; |
short myNumEffects; |
short myIndex; |
OSErr myErr = noErr; |
// get a list of the available effects |
myErr = QTNewAtomContainer(&myEffectsList); |
if (myErr != noErr) |
goto bail; |
myErr = QTGetEffectsList(&myEffectsList, kNoMinNumSources, kNoMaxNumSources, 0L); |
if (myErr != noErr) |
goto bail; |
// the returned effects list contains (at least) two atoms for each available effect component, |
// a name atom and a type atom; happily, this list is already sorted alphabetically by effect name |
myNumEffects = QTCountChildrenOfType(myEffectsList, kParentAtomIsContainer, kEffectNameAtom); |
for (myIndex = 1; myIndex <= myNumEffects; myIndex++) { |
QTAtom myNameAtom = 0L; |
QTAtom myTypeAtom = 0L; |
myNameAtom = QTFindChildByIndex(myEffectsList, kParentAtomIsContainer, kEffectNameAtom, myIndex, NULL); |
myTypeAtom = QTFindChildByIndex(myEffectsList, kParentAtomIsContainer, kEffectTypeAtom, myIndex, NULL); |
if ((myNameAtom != 0L) && (myTypeAtom != 0L)) { |
char myName[256]; |
OSType myType; |
long mySize; |
// get the data from the type and name atoms |
QTCopyAtomDataToPtr(myEffectsList, myTypeAtom, false, sizeof(myType), &myType, NULL); |
QTCopyAtomDataToPtr(myEffectsList, myNameAtom, true, sizeof(myName), myName, &mySize); |
myName[mySize] = '\0'; |
QTEffects_AddItemToPopUpMenu(&gSelectEffectPopup, myName, myType); |
} |
} |
bail: |
QTDisposeAtomContainer(myEffectsList); |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Effects utilities. |
// |
// Use these functions to set up and run QuickTime video effects. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_CreateEffectDescription |
// Create an effect description for zero, one, or two 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 QTEffects_CreateEffectDescription (OSType theEffectName, OSType theSourceName1, OSType theSourceName2) |
{ |
QTAtomContainer myEffectDesc = NULL; |
OSType myType = EndianU32_NtoB(theEffectName); |
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 it's not kSourceNoneName |
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 it's not kSourceNoneName |
if (theSourceName2 != kSourceNoneName) { |
myType = EndianU32_NtoB(theSourceName2); |
myErr = QTInsertChild(myEffectDesc, kParentAtomIsContainer, kEffectSourceName, 2, 0, sizeof(myType), &myType, NULL); |
} |
bail: |
return(myEffectDesc); |
} |
////////// |
// |
// QTEffects_AddTrackReferenceToInputMap |
// Add a track reference to the specified input map. |
// |
////////// |
OSErr QTEffects_AddTrackReferenceToInputMap (QTAtomContainer theInputMap, Track theTrack, Track theSrcTrack, OSType theSrcName) |
{ |
OSErr myErr = noErr; |
QTAtom myInputAtom; |
long myRefIndex; |
OSType myType; |
myErr = AddTrackReference(theTrack, theSrcTrack, kTrackModifierReference, &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); |
} |
////////// |
// |
// QTEffects_SetUpEffectSequence |
// Set up an effects sequence. |
// |
////////// |
OSErr QTEffects_SetUpEffectSequence (void) |
{ |
OSErr myErr = noErr; |
ImageSequenceDataSource mySrc1 = 0; |
ImageSequenceDataSource mySrc2 = 0; |
ImageSequenceDataSource mySrc3 = 0; |
PixMapHandle mySrcPixMap; |
PixMapHandle myDstPixMap; |
// if an effect sequence is already set up, end it |
if (gCurrentState.fEffectSequenceID != 0L) { |
CDSequenceEnd(gCurrentState.fEffectSequenceID); |
gCurrentState.fEffectSequenceID = 0L; |
} |
// if there is a timebase already set up, dispose of it |
if (gCurrentState.fTimeBase != NULL) { |
DisposeTimeBase(gCurrentState.fTimeBase); |
gCurrentState.fTimeBase = NULL; |
} |
// make an effects sequence |
HLock((Handle)gCurrentState.fEffectDescription); |
// prepare the decompression sequence for playback |
myErr = DecompressSequenceBeginS( |
&gCurrentState.fEffectSequenceID, |
gCurrentState.fSampleDescription, |
#if TARGET_CPU_68K |
StripAddress(*gCurrentState.fEffectDescription), |
#else |
*gCurrentState.fEffectDescription, |
#endif |
GetHandleSize(gCurrentState.fEffectDescription), |
(CGrafPtr)GetWindowPort(gMainWindow), |
NULL, |
NULL, |
NULL, |
ditherCopy, |
NULL, |
0, |
codecNormalQuality, |
NULL); |
HUnlock((Handle)gCurrentState.fEffectDescription); |
if (myErr != noErr) |
goto bail; |
// get the pixel maps for the GWorlds |
mySrcPixMap = GetGWorldPixMap(gGW1); |
myDstPixMap = GetGWorldPixMap(gGW2); |
if ((mySrcPixMap == NULL) || (myDstPixMap == NULL)) |
goto bail; |
// make the first effect source |
if (gGW1 == NULL) |
goto bail; |
myErr = MakeImageDescriptionForPixMap(mySrcPixMap, &gGW1Desc); |
if (myErr != noErr) |
goto bail; |
myErr = CDSequenceNewDataSource(gCurrentState.fEffectSequenceID, &mySrc1, kSourceOneName, 1, (Handle)gGW1Desc, NULL, 0); |
if (myErr != noErr) |
goto bail; |
CDSequenceSetSourceData(mySrc1, GetPixBaseAddr(mySrcPixMap), (**gGW1Desc).dataSize); |
// make the second effect source |
if (gGW2 == NULL) |
goto bail; |
myErr = MakeImageDescriptionForPixMap(myDstPixMap, &gGW2Desc); |
if (myErr != noErr) |
goto bail; |
myErr = CDSequenceNewDataSource(gCurrentState.fEffectSequenceID, &mySrc2, kSourceTwoName, 1, (Handle)gGW2Desc, NULL, 0); |
if (myErr != noErr) |
goto bail; |
CDSequenceSetSourceData(mySrc2, GetPixBaseAddr(myDstPixMap), (**gGW2Desc).dataSize); |
// create a new time base and associate it with the decompression sequence |
gCurrentState.fTimeBase = NewTimeBase(); |
myErr = GetMoviesError(); |
if (myErr != noErr) |
goto bail; |
SetTimeBaseRate(gCurrentState.fTimeBase, 0); |
myErr = CDSequenceSetTimeBase(gCurrentState.fEffectSequenceID, gCurrentState.fTimeBase); |
bail: |
return(myErr); |
} |
////////// |
// |
// QTEffects_MakeSampleDescription |
// Return a new image description with default and specified values. |
// |
////////// |
ImageDescriptionHandle QTEffects_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); |
} |
////////// |
// |
// QTEffects_RunEffect |
// Run the effect: decompress a single step of the effect sequence. |
// |
////////// |
OSErr QTEffects_RunEffect (TimeValue theTime) |
{ |
OSErr myErr = noErr; |
ICMFrameTimeRecord myFrameTime; |
// assertions |
if ((gCurrentState.fEffectDescription == NULL) || (gCurrentState.fEffectSequenceID == 0L)) |
goto bail; |
// set the timebase time to the step of the sequence to be rendered |
SetTimeBaseValue(gCurrentState.fTimeBase, theTime, gNumberOfSteps); |
myFrameTime.value.hi = 0; |
myFrameTime.value.lo = theTime; |
myFrameTime.scale = gNumberOfSteps; |
myFrameTime.base = 0; |
myFrameTime.duration = gNumberOfSteps; |
myFrameTime.rate = 0; |
myFrameTime.recordSize = sizeof(myFrameTime); |
myFrameTime.frameNumber = 1; |
myFrameTime.flags = icmFrameTimeHasVirtualStartTimeAndDuration; |
myFrameTime.virtualStartTime.lo = 0; |
myFrameTime.virtualStartTime.hi = 0; |
myFrameTime.virtualDuration = gNumberOfSteps; |
HLock((Handle)gCurrentState.fEffectDescription); |
myErr = DecompressSequenceFrameWhen( |
gCurrentState.fEffectSequenceID, |
#if TARGET_CPU_68K |
StripAddress(*((Handle)gCurrentState.fEffectDescription)), |
#else |
*((Handle)gCurrentState.fEffectDescription), |
#endif |
GetHandleSize((Handle)gCurrentState.fEffectDescription), |
0, |
0, |
NULL, |
&myFrameTime); |
HUnlock((Handle)gCurrentState.fEffectDescription); |
if (myErr != noErr) |
goto bail; |
bail: |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// General imaging utilities. |
// |
// Use these functions to draw pictures into GWorlds and do other imaging operations. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_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 QTEffects_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); |
} |
////////// |
// |
// QTEffects_GetPictureAsGWorld |
// Prompt the user to select a picture, create a new GWorld of the specified size and bit depth, |
// and then draw the picture into it. The new GWorld is returned through the theGW parameter. |
// |
////////// |
OSErr QTEffects_GetPictureAsGWorld (short theWidth, short theHeight, short theDepth, GWorldPtr *theGW) |
{ |
FSSpec myFSSpec; |
OSType myTypeList[] = {kQTFileTypeQuickTimeImage}; |
short myNumTypes = 1; |
GraphicsImportComponent myImporter = NULL; |
Rect myRect; |
QTFrameFileFilterUPP myFileFilterUPP = NULL; |
OSErr myErr = paramErr; |
#if TARGET_OS_MAC |
myNumTypes = 0; |
#endif |
// have the user select an image file |
myFileFilterUPP = QTFrame_GetFileFilterUPP((ProcPtr)QTFrame_FilterFiles); |
myErr = QTFrame_GetOneFileWithPreview(myNumTypes, (QTFrameTypeListPtr)myTypeList, &myFSSpec, myFileFilterUPP); |
if (myErr != noErr) |
goto bail; |
// get a graphics importer for the image file |
myErr = GetGraphicsImporterForFile(&myFSSpec, &myImporter); |
if (myErr != noErr) |
goto bail; |
if (*theGW != NULL) { |
DisposeGWorld(*theGW); |
*theGW = NULL; |
} |
// 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; |
// draw the picture into the GWorld |
GraphicsImportSetGWorld(myImporter, *theGW, NULL); |
GraphicsImportSetBoundsRect(myImporter, &myRect); |
GraphicsImportDraw(myImporter); |
bail: |
if (myFileFilterUPP != NULL) |
DisposeNavObjectFilterUPP(myFileFilterUPP); |
if (myImporter != NULL) |
CloseComponent(myImporter); |
return(myErr); |
} |
////////// |
// |
// QTEffects_AddVideoTrackFromGWorld |
// Add to the specified movie a video track for the specified picture resource. |
// |
////////// |
OSErr QTEffects_AddVideoTrackFromGWorld (Movie *theMovie, GWorldPtr theGW, Track *theSourceTrack, long theStartTime, short theWidth, short theHeight) |
{ |
Media myMedia; |
ImageDescriptionHandle myDesc = 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, FixRatio(theWidth, 1), FixRatio(theHeight, 1), kNoVolume); |
myMedia = NewTrackMedia(*theSourceTrack, VideoMediaType, kOneSecond, NULL, 0); |
// get the rectangle for the movie |
GetMovieBox(*theMovie, &myRect); |
// begin editing the new track |
BeginMediaEdits(myMedia); |
// 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 |
myDesc = (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, kAnimationCodecType, 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, kAnimationCodecType, myDesc, myDataPtr); |
if (myErr != noErr) |
goto bail; |
myErr = AddMediaSample(myMedia, myData, 0, (**myDesc).dataSize, kEffectMovieDuration, (SampleDescriptionHandle)myDesc, 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 (myDesc != NULL) |
DisposeHandle((Handle)myDesc); |
// if (mySrcPixMap != NULL) |
// UnlockPixels(mySrcPixMap); |
if (myDstPixMap != NULL) |
UnlockPixels(myDstPixMap); |
if (myGWorld != NULL) |
DisposeGWorld(myGWorld); |
return(myErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Movie utilities. |
// |
// Use these functions to create movie files and do other movie operations. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QTEffects_CreateEffectsMovie |
// Create a movie containing the current video effect transition from one picture to another. |
// |
////////// |
void QTEffects_CreateEffectsMovie (OSType theEffectType, QTAtomContainer theEffectDesc, short theWidth, short theHeight) |
{ |
ImageDescriptionHandle mySampleDesc = NULL; |
short myResRefNum = 0; |
short myResID = movieInDataForkResID; |
Movie myMovie = NULL; |
Track myTrack = NULL; |
Track mySrc1Track = NULL; |
Track mySrc2Track = NULL; |
Media myMedia; |
FSSpec myFile; |
Boolean myIsSelected = false; |
Boolean myIsReplacing = false; |
QTAtomContainer myInputMap = NULL; |
TimeValue mySampleTime; |
long myFlags = createMovieFileDeleteCurFile | createMovieFileDontCreateResFile; |
StringPtr myMoviePrompt = QTUtils_ConvertCToPascalString(kSaveEffectMoviePrompt); |
StringPtr myMovieFileName = QTUtils_ConvertCToPascalString(kSaveEffectMovieFileName); |
OSErr myErr = noErr; |
// create an effect sample description |
mySampleDesc = QTEffects_MakeSampleDescription(theEffectType, theWidth, theHeight); |
if (mySampleDesc == NULL) |
goto bail; |
// prompt user for new file name |
QTFrame_PutFile(myMoviePrompt, myMovieFileName, &myFile, &myIsSelected, &myIsReplacing); |
if (!myIsSelected) |
goto bail; // deal with user cancelling |
// create a movie file for the destination movie |
myErr = CreateMovieFile(&myFile, sigMoviePlayer, smCurrentScript, myFlags, &myResRefNum, &myMovie); |
if (myErr != noErr) |
goto bail; |
// add the video tracks of the source pictures to the effects movie; |
// the video tracks used as sources for the effect should start at the same time as the effect track |
// and end at the same time as the effect track |
myErr = QTEffects_AddVideoTrackFromGWorld(&myMovie, gGW1, &mySrc1Track, 0, theWidth, theHeight); |
if (myErr != noErr) |
goto bail; |
myErr = QTEffects_AddVideoTrackFromGWorld(&myMovie, gGW2, &mySrc2Track, 0, theWidth, theHeight); |
if (myErr != noErr) |
goto bail; |
// create the video effect track and media |
myTrack = NewMovieTrack(myMovie, FixRatio(theWidth, 1), FixRatio(theHeight, 1), kNoVolume); |
myMedia = NewTrackMedia(myTrack, VideoMediaType, kOneSecond, NULL, 0); |
// add the effect description as a sample to the effect track media |
BeginMediaEdits(myMedia); |
myErr = AddMediaSample(myMedia, (Handle)theEffectDesc, 0, GetHandleSize((Handle)theEffectDesc), kEffectMovieDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, &mySampleTime); |
if (myErr != noErr) |
goto bail; |
EndMediaEdits(myMedia); |
// create the input map and add references for the two video tracks |
myErr = QTNewAtomContainer(&myInputMap); |
if (myErr != noErr) |
goto bail; |
myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, mySrc1Track, kSourceOneName); |
if (myErr != noErr) |
goto bail; |
myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, mySrc2Track, kSourceTwoName); |
if (myErr != noErr) |
goto bail; |
// add the input map to the effects track |
myErr = SetMediaInputMap(myMedia, myInputMap); |
if (myErr != noErr) |
goto bail; |
// add the media to the track |
myErr = InsertMediaIntoTrack(myTrack, 0, mySampleTime, GetMediaDuration(myMedia), fixed1); |
if (myErr != noErr) |
goto bail; |
#if ALLOW_COMPOUND_EFFECTS |
QTEffects_AddFilmNoiseToMovie(myMovie, myTrack); |
#endif |
#ifdef __QTUtilities__ |
// save the current looping state |
myErr = QTUtils_SetMovieFileLoopingInfo(myMovie, (gLoopingState - (kSettingsMenuResID << 8)) - 2); |
#endif |
// put the movie resource into the file |
myErr = AddMovieResource(myMovie, myResRefNum, &myResID, NULL); |
bail: |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
if (myResRefNum != 0) |
CloseMovieFile(myResRefNum); |
if (myMovie != NULL) |
DisposeMovie(myMovie); |
if (myInputMap != NULL) |
QTDisposeAtomContainer(myInputMap); |
free(myMoviePrompt); |
free(myMovieFileName); |
return; |
} |
////////// |
// |
// QTEffects_AddFilmNoiseToMovie |
// Add the film noise filter to the specified track. |
// |
////////// |
void QTEffects_AddFilmNoiseToMovie (Movie theMovie, Track theSrcTrack) |
{ |
ImageDescriptionHandle mySampleDesc = NULL; |
Track myTrack = NULL; |
Media myMedia = NULL; |
QTAtomContainer myInputMap = NULL; |
QTAtomContainer myEffectDesc = NULL; |
TimeValue mySampleTime; |
Fixed myWidth, myHeight; |
OSErr myErr = noErr; |
// get width and height of track |
GetTrackDimensions(theSrcTrack, &myWidth, &myHeight); |
// create an effect sample description |
mySampleDesc = QTEffects_MakeSampleDescription(kFilmNoiseImageFilterType, myWidth >> 16, myHeight >> 16); |
if (mySampleDesc == NULL) |
goto bail; |
// create the video effect track and media |
myTrack = NewMovieTrack(theMovie, myWidth, myHeight, kNoVolume); |
myMedia = NewTrackMedia(myTrack, VideoMediaType, kOneSecond, NULL, 0); |
// create an effect description |
myEffectDesc = QTEffects_CreateEffectDescription(kFilmNoiseImageFilterType, kSourceThreeName, kSourceNoneName); |
// add the effect description as a sample to the effect track media |
BeginMediaEdits(myMedia); |
myErr = AddMediaSample(myMedia, (Handle)myEffectDesc, 0, GetHandleSize((Handle)myEffectDesc), kEffectMovieDuration, (SampleDescriptionHandle)mySampleDesc, 1, 0, &mySampleTime); |
if (myErr != noErr) |
goto bail; |
EndMediaEdits(myMedia); |
// create the input map and add references for the first effect track |
myErr = QTNewAtomContainer(&myInputMap); |
if (myErr != noErr) |
goto bail; |
myErr = QTEffects_AddTrackReferenceToInputMap(myInputMap, myTrack, theSrcTrack, kSourceThreeName); |
if (myErr != noErr) |
goto bail; |
// add the input map to the effects track |
myErr = SetMediaInputMap(myMedia, myInputMap); |
if (myErr != noErr) |
goto bail; |
// add the media to the track |
myErr = InsertMediaIntoTrack(myTrack, 0, mySampleTime, GetMediaDuration(myMedia), fixed1); |
if (myErr != noErr) |
goto bail; |
bail: |
if (mySampleDesc != NULL) |
DisposeHandle((Handle)mySampleDesc); |
if (myInputMap != NULL) |
QTDisposeAtomContainer(myInputMap); |
return; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-02-25