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.
VRMovies.c
////////// |
// |
// File: VRMovies.c |
// |
// Contains: Support for QuickTime movie playback in VR nodes. |
// |
// Written by: Tim Monroe |
// Some code borrowed from QTVRSamplePlayer by Bryce Wolfson. |
// |
// Copyright: © 1996-1998 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <16> 03/20/00 rtm made changes to get things running under CarbonLib |
// <15> 06/23/99 rtm added VRMoov_MacRGBToWinRGB and VRMoov_WinRGBToMacRGB |
// <14> 06/22/99 rtm more work on matrices; now everything works okay (AFAICT) |
// <13> 06/21/99 rtm added call to UpdateMovie to back buffer imaging procedure |
// <12> 06/18/99 rtm got movie matrices working correctly; added color picking to Windows |
// <11> 06/17/99 rtm reworked geometry of back buffer yet again; now we copy from the |
// offscreen GWorld to back buffer using use DecompressSequenceFrameS |
// <10> 05/16/99 rtm added VRMoov_DumpWindowData; further work on geometry of back buffer |
// <9> 05/15/99 rtm reworked geometry handling to support horizontal back buffer in QT 4.0 |
// <8> 05/04/98 rtm added automatic rotation of movie |
// <7> 03/06/97 rtm started to implement video masking |
// <6> 03/05/97 rtm added VRMoov_SetChromaColor; added fChromaColor to app data record |
// <5> 03/04/97 rtm fixed compositing problems at back buffer edges |
// <4> 03/03/97 rtm added VRMoov_SetVideoGraphicsMode to handle compositing |
// without using an offscreen GWorld |
// <3> 02/27/97 rtm further development: borrowed some ideas from QTVRSamplePlayer; |
// added VRMoov_SetEmbeddedMovieWidth etc. |
// <2> 12/12/96 rtm further development: borrowed some ideas from BoxMoov demo |
// <1> 12/11/96 rtm first file |
// |
// This code draws the QuickTime movie frames into the back buffer, either directly |
// or indirectly (via an offscreen GWorld). Direct drawing gives the best performance, |
// but indirect drawing is necessary for some visual effects. |
// |
////////// |
// TO DO: |
// + finish video masking by custom 'hide' hot spots (so video goes *behind* such hot spots) |
// + verify that everything works okay if *images* are used instead of movies (scaling seems to be a problem) |
////////// |
// |
// header files |
// |
////////// |
#include "VRMovies.h" |
////////// |
// |
// constants |
// |
////////// |
const RGBColor kClearColor = {0x0000, 0xffff, 0x0000}; // the default chroma key color |
const RGBColor kBlackColor = {0x0000, 0x0000, 0x0000}; |
const RGBColor kWhiteColor = {0xffff, 0xffff, 0xffff}; |
////////// |
// |
// global variables |
// |
////////// |
#if TARGET_OS_MAC |
UserEventUPP gColorFilterUPP = NULL; // UPP to our custom color picker dialog event filter |
#endif |
////////// |
// |
// VRMoov_InitWindowData |
// Initialize any window-specific data. |
// |
////////// |
ApplicationDataHdl VRMoov_InitWindowData (WindowObject theWindowObject) |
{ |
#pragma unused(theWindowObject) |
ApplicationDataHdl myAppData; |
myAppData = (ApplicationDataHdl)NewHandleClear(sizeof(ApplicationDataRecord)); |
if (myAppData != NULL) { |
(**myAppData).fMovie = NULL; |
(**myAppData).fOffscreenGWorld = NULL; |
(**myAppData).fOffscreenPixMap = NULL; |
(**myAppData).fPrevBBufGWorld = NULL; |
(**myAppData).fMovieCenter.x = 0.0; |
(**myAppData).fMovieCenter.y = 0.0; |
(**myAppData).fMovieScale = 1.0; |
(**myAppData).fMovieWidth = kDefaultEmbMovieWidth; |
(**myAppData).fUseOffscreenGWorld = false; |
(**myAppData).fUseMovieCenter = true; |
(**myAppData).fQTMovieHasSound = false; |
(**myAppData).fCompositeMovie = false; |
(**myAppData).fUseHideRegion = false; |
(**myAppData).fChromaColor = kClearColor; |
(**myAppData).fHideRegion = NULL; |
(**myAppData).fBackBufferIsHoriz = false; |
(**myAppData).fImageDesc = NULL; |
(**myAppData).fImageSequence = 0; |
SetIdentityMatrix(&(**myAppData).fMovieMatrix); |
SetIdentityMatrix(&(**myAppData).fOrigMovieMatrix); |
// create a new routine descriptor |
(**myAppData).fBackBufferProc = NewQTVRBackBufferImagingUPP(VRMoov_BackBufferImagingProc); |
} |
#if TARGET_OS_MAC |
if (gColorFilterUPP == NULL) |
gColorFilterUPP = NewUserEventUPP(VRMoov_ColorDialogEventFilter); |
#endif |
return(myAppData); |
} |
////////// |
// |
// VRMoov_DumpWindowData |
// Dispose of any window-specific data. |
// |
////////// |
void VRMoov_DumpWindowData (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData = NULL; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return; |
VRMoov_DumpEmbeddedMovie(theWindowObject); |
DisposeQTVRBackBufferImagingUPP((**myAppData).fBackBufferProc); |
} |
/////////// |
// |
// VRMoov_GetEmbeddedMovie |
// Get the QuickTime movie to be embedded in a panorama. |
// Returns a Boolean to indicate success (true) or failure (false). |
// |
////////// |
Boolean VRMoov_GetEmbeddedMovie (WindowObject theWindowObject) |
{ |
FSSpec myFSSpec; |
OSType myTypeList = kQTFileTypeMovie; |
OSErr myErr = paramErr; |
// do some preliminary parameter checking |
if (theWindowObject == NULL) |
return(false); |
if (!QTFrame_IsWindowObjectOurs(theWindowObject)) |
return(false); |
// elicit the movie file from user |
myErr = QTFrame_GetOneFileWithPreview(1, (QTFrameTypeListPtr)&myTypeList, &myFSSpec, NULL); |
if (myErr != noErr) { |
VRMoov_DumpEmbeddedMovie(theWindowObject); // clean up any existing embedded movie |
return(false); |
} |
return(VRMoov_LoadEmbeddedMovie(&myFSSpec, theWindowObject)); |
} |
////////// |
// |
// VRMoov_LoadEmbeddedMovie |
// Load the QuickTime movie in the specified file. |
// Returns a Boolean to indicate success (true) or failure (false). |
// |
////////// |
Boolean VRMoov_LoadEmbeddedMovie (FSSpec *theMovieFile, WindowObject theWindowObject) |
{ |
short myMovieFileRef; |
Movie myMovie; |
GWorldPtr myGWorld = NULL; |
Rect myRect; |
ApplicationDataHdl myAppData = NULL; |
OSErr myErr = paramErr; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
goto bail; |
HLock((Handle)myAppData); |
////////// |
// |
// open the movie file and the movie |
// |
////////// |
myErr = OpenMovieFile(theMovieFile, &myMovieFileRef, fsRdPerm); |
if (myErr != noErr) |
goto bail; |
myErr = NewMovieFromFile(&myMovie, myMovieFileRef, NULL, (StringPtr)NULL, newMovieActive, NULL); |
if (myErr != noErr) |
goto bail; |
////////// |
// |
// get the movie geometry |
// |
////////// |
GetMovieBox(myMovie, &myRect); |
GetMovieMatrix(myMovie, &(**myAppData).fOrigMovieMatrix); |
MacOffsetRect(&myRect, -myRect.left, -myRect.top); |
SetMovieBox(myMovie, &myRect); |
// keep track of the movie and movie rectangle (in our app-specific data structure) |
(**myAppData).fMovie = myMovie; |
(**myAppData).fMovieBox = myRect; |
// determine the orientation of the back buffer |
(**myAppData).fBackBufferIsHoriz = QTVRUtils_IsBackBufferHorizontal((**theWindowObject).fInstance); |
// get rid of any existing offscreen graphics world |
if ((**myAppData).fOffscreenGWorld != NULL) { |
DisposeGWorld((**myAppData).fOffscreenGWorld); |
(**myAppData).fOffscreenGWorld = NULL; |
} |
// clear out any existing custom cover/uncover functions and reset the video media graphics mode |
// (these may have been modified for direct-screen drawing) |
SetMovieCoverProcs(myMovie, NULL, NULL, 0L); |
VRMoov_SetVideoGraphicsMode(myMovie, myAppData, false); |
////////// |
// |
// if necessary, create an offscreen graphics world |
// |
// this is where we'll image the movie before copying it into the back buffer |
// when we want to do special effects |
// |
////////// |
if ((**myAppData).fUseOffscreenGWorld) { |
myErr = NewGWorld(&myGWorld, 0, &myRect, NULL, NULL, 0); |
(**myAppData).fOffscreenGWorld = myGWorld; |
(**myAppData).fOffscreenPixMap = GetGWorldPixMap(myGWorld); |
// make an image description, which is needed by DecompressSequenceBegin |
LockPixels((**myAppData).fOffscreenPixMap); |
MakeImageDescriptionForPixMap((**myAppData).fOffscreenPixMap, &((**myAppData).fImageDesc)); |
UnlockPixels((**myAppData).fOffscreenPixMap); |
} else { |
// set the video media graphics mode to drop out the chroma key color in a movie; |
// we also need to install an uncover function that doesn't erase the uncovered region |
if ((**myAppData).fCompositeMovie) { |
VRMoov_SetVideoGraphicsMode(myMovie, myAppData, true); |
SetMovieCoverProcs(myMovie, NewMovieRgnCoverUPP(VRMoov_UncoverProc), NULL, (long)theWindowObject); |
} |
} |
////////// |
// |
// install the back-buffer imaging procedure |
// |
////////// |
if ((**theWindowObject).fInstance != NULL) |
myErr = VRMoov_InstallBackBufferImagingProc((**theWindowObject).fInstance, theWindowObject); |
// start the movie playing in a loop |
VRMoov_LoopEmbeddedMovie(myMovie); |
bail: |
// we don't want to edit the embedded movie, so we can close the movie file |
if (myMovieFileRef != 0) |
CloseMovieFile(myMovieFileRef); |
HUnlock((Handle)myAppData); |
return(myErr == noErr); |
} |
////////// |
// |
// VRMoov_LoopEmbeddedMovie |
// Start the QuickTime movie playing in a loop. |
// |
////////// |
void VRMoov_LoopEmbeddedMovie (Movie theMovie) |
{ |
TimeBase myTimeBase; |
// throw the movie into loop mode |
myTimeBase = GetMovieTimeBase(theMovie); |
SetTimeBaseFlags(myTimeBase, GetTimeBaseFlags(myTimeBase) | loopTimeBase); |
// start playing the movie |
StartMovie(theMovie); |
} |
////////// |
// |
// VRMoov_DumpEmbeddedMovie |
// Stop any existing embedded movie from playing and then clean up. |
// |
////////// |
void VRMoov_DumpEmbeddedMovie (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData; |
if (theWindowObject == NULL) |
goto bail; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
goto bail; |
// if we have an embedded movie, stop it from playing and dispose of it |
if ((**myAppData).fMovie != NULL) { |
StopMovie((**myAppData).fMovie); |
DisposeMovie((**myAppData).fMovie); |
(**myAppData).fMovie = NULL; |
} |
// get rid of any existing offscreen graphics world |
if ((**myAppData).fOffscreenGWorld != NULL) { |
DisposeGWorld((**myAppData).fOffscreenGWorld); |
(**myAppData).fOffscreenGWorld = NULL; |
} |
// clear the existing back buffer imaging proc |
QTVRSetBackBufferImagingProc((**theWindowObject).fInstance, NULL, 0, NULL, 0); |
// clear out any other movie-specific data |
(**myAppData).fPrevBBufGWorld = NULL; |
MacSetRect(&(**myAppData).fPrevBBufRect, 0, 0, 0, 0); |
MacSetRect(&(**myAppData).fMovieBox, 0, 0, 0, 0); |
SetIdentityMatrix(&(**myAppData).fMovieMatrix); |
SetIdentityMatrix(&(**myAppData).fOrigMovieMatrix); |
if ((**myAppData).fImageDesc != NULL) { |
DisposeHandle((Handle)(**myAppData).fImageDesc); |
(**myAppData).fImageDesc = NULL; |
} |
VRMoov_RemoveDecompSeq(theWindowObject); |
// make sure the back buffer is clean |
QTVRRefreshBackBuffer((**theWindowObject).fInstance, 0); |
bail: |
return; |
} |
////////// |
// |
// VRMoov_InstallBackBufferImagingProc |
// Install a back buffer imaging procedure. |
// (This routine might sometimes be called to move or resize the area of interest within the panorama.) |
// |
////////// |
OSErr VRMoov_InstallBackBufferImagingProc (QTVRInstance theInstance, WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData; |
QTVRAreaOfInterest myArea; |
float myWidth, myHeight; |
OSErr myErr = noErr; |
////////// |
// |
// initialize; clean up any existing back buffer procedure |
// |
////////// |
if ((theInstance == NULL) || (theWindowObject == NULL)) |
return(paramErr); |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return(paramErr); |
HLock((Handle)myAppData); |
// remove any existing back buffer imaging procedure |
if ((**myAppData).fBackBufferProc != NULL) |
QTVRSetBackBufferImagingProc(theInstance, NULL, 0, NULL, 0); |
////////// |
// |
// set the area of interest |
// |
// the panAngle and tiltAngle fields define the top-left corner, in panorama space, of the area of interest; |
// so here we do not have to worry about whether the back buffer is oriented vertically or horizontally |
// |
////////// |
// the application data structure holds the desired width, center, size, and scale of the movie |
myWidth = (**myAppData).fMovieWidth * (**myAppData).fMovieScale; |
myHeight = myWidth * (((float)(**myAppData).fMovieBox.bottom) / ((float)(**myAppData).fMovieBox.right)); |
if ((**myAppData).fUseMovieCenter) { |
// use the stored movie center |
myArea.panAngle = (**myAppData).fMovieCenter.x + (myWidth/2); |
myArea.tiltAngle = (**myAppData).fMovieCenter.y + (myHeight/2); |
} else { |
// center the movie on the current pan and tilt angles |
myArea.panAngle = QTVRGetPanAngle(theInstance) + (myWidth/2); |
myArea.tiltAngle = QTVRGetTiltAngle(theInstance) + (myHeight/2); |
} |
myArea.width = myWidth; |
myArea.height = myHeight; |
////////// |
// |
// set the back buffer flags and install the back buffer procedure |
// |
////////// |
// make sure we get called on every idle event, so we can keep playing the embedded movie; |
// also make sure we get called on every back buffer update |
if ((**myAppData).fCompositeMovie) |
myArea.flags = kQTVRBackBufferEveryIdle | kQTVRBackBufferEveryUpdate | kQTVRBackBufferAlwaysRefresh; |
else |
myArea.flags = kQTVRBackBufferEveryIdle | kQTVRBackBufferEveryUpdate; |
// if the back buffer is oriented horizontally, set the appropriate flag |
if ((**myAppData).fBackBufferIsHoriz) |
myArea.flags |= kQTVRBackBufferHorizontal; |
// install our procedure |
myErr = QTVRSetBackBufferImagingProc(theInstance, (**myAppData).fBackBufferProc, 1, &myArea, (SInt32)theWindowObject); |
HUnlock((Handle)myAppData); |
return(myErr); |
} |
////////// |
// |
// VRMoov_CalcImagingMatrix |
// Calculate the movie matrix required to draw the embedded movie into the specified rectangle. |
// |
////////// |
OSErr VRMoov_CalcImagingMatrix (WindowObject theWindowObject, Rect *theBBufRect) |
{ |
ApplicationDataHdl myAppData = NULL; |
Rect myDestRect = *theBBufRect; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return(paramErr); |
// reset the current movie matrix with the original movie matrix of the embedded movie; |
// we need to preserve that matrix in our calculations below if we are not drawing the |
// movie into an offscreen GWorld |
(**myAppData).fMovieMatrix = (**myAppData).fOrigMovieMatrix; |
// in general, it's easiest to construct the desired matrix by first doing the scaling |
// and then doing the rotation and translation (if necessary); so we need to swap the |
// right and bottom edges of the back buffer rectangle before doing the scaling, if a |
// rotation will also be necessary |
if (!(**myAppData).fBackBufferIsHoriz) { |
myDestRect.bottom = theBBufRect->right; |
myDestRect.right = theBBufRect->bottom; |
} |
// set up the scaling matrix |
// (MapMatrix concatenates the new matrix to the existing matrix, whereas RectMatrix first |
// sets the existing matrix to the identity matrix) |
if ((**myAppData).fUseOffscreenGWorld) |
RectMatrix(&(**myAppData).fMovieMatrix, &(**myAppData).fMovieBox, &myDestRect); |
else |
MapMatrix(&(**myAppData).fMovieMatrix, &(**myAppData).fMovieBox, &myDestRect); |
// add a rotation and translation, if necessary |
if (!(**myAppData).fBackBufferIsHoriz) { |
RotateMatrix(&(**myAppData).fMovieMatrix, Long2Fix(-90), 0, 0); |
TranslateMatrix(&(**myAppData).fMovieMatrix, 0, Long2Fix(RECT_HEIGHT(*theBBufRect))); |
} |
return(noErr); |
} |
////////// |
// |
// VRMoov_SetupDecompSeq |
// Set up the decompression sequence for DecompressionSequenceFrameS. |
// |
// This needs to be called whenever either the rectangle or the GWorld of the back buffer changes. |
// |
////////// |
OSErr VRMoov_SetupDecompSeq (WindowObject theWindowObject, GWorldPtr theDestGWorld) |
{ |
ApplicationDataHdl myAppData = NULL; |
short myMode = srcCopy; |
OSErr myErr = noErr; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return(paramErr); |
// make sure we don't have a decompression sequence already open |
VRMoov_RemoveDecompSeq(theWindowObject); |
// set up the transfer mode |
if ((**myAppData).fCompositeMovie) |
myMode = srcCopy | transparent; |
// set up the image decompression sequence |
myErr = DecompressSequenceBegin( &(**myAppData).fImageSequence, |
(**myAppData).fImageDesc, |
(CGrafPtr)theDestGWorld, |
NULL, |
NULL, // entire source image |
&(**myAppData).fMovieMatrix, |
myMode, |
NULL, // no mask |
0, |
(**(**myAppData).fImageDesc).spatialQuality, |
NULL); |
return(myErr); |
} |
////////// |
// |
// VRMoov_RemoveDecompSeq |
// Remove the decompression sequence for DecompressionSequenceFrameS. |
// |
////////// |
OSErr VRMoov_RemoveDecompSeq (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData = NULL; |
OSErr myErr = paramErr; |
if (theWindowObject != NULL) { |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData != NULL) |
if ((**myAppData).fImageSequence != 0) { |
myErr = CDSequenceEnd((**myAppData).fImageSequence); |
(**myAppData).fImageSequence = 0; |
} |
} |
return(myErr); |
} |
////////// |
// |
// VRMoov_BackBufferImagingProc |
// The back buffer imaging procedure: get a frame of movie and image it into the back buffer. |
// Also, do any additional compositing that might be desired. |
// |
////////// |
PASCAL_RTN OSErr VRMoov_BackBufferImagingProc (QTVRInstance theInstance, Rect *theRect, UInt16 theAreaIndex, UInt32 theFlagsIn, UInt32 *theFlagsOut, long theRefCon) |
{ |
#pragma unused(theAreaIndex) |
WindowObject myWindowObject = (WindowObject)theRefCon; |
ApplicationDataHdl myAppData = NULL; |
Movie myMovie = NULL; |
Boolean myIsDrawing = theFlagsIn & kQTVRBackBufferRectVisible; |
GWorldPtr myBBufGWorld, myMovGWorld; |
GDHandle myBBufGDevice, myMovGDevice; |
Rect myRect; |
OSErr myErr = paramErr; |
////////// |
// |
// initialize; make sure that we've got the data we need to continue |
// |
////////// |
// assume we're not going to draw anything |
*theFlagsOut = 0; |
if ((theInstance == NULL) || (myWindowObject == NULL)) |
goto bail; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject); |
if (myAppData == NULL) |
goto bail; |
HLock((Handle)myAppData); |
myMovie = (**myAppData).fMovie; |
if (myMovie == NULL) { |
// we don't have an embedded movie, so remove this back-buffer imaging procedure |
VRMoov_DumpEmbeddedMovie(myWindowObject); |
goto bail; |
} |
////////// |
// |
// make sure that the movie GWorld is set correctly; |
// note that we call SetMovieGWorld only if we have to (for performance reasons) |
// |
////////// |
// get the current graphics world |
// (on entry, the current graphics world is [usually] set to the back buffer) |
GetGWorld(&myBBufGWorld, &myBBufGDevice); |
// get the embedded movie's graphics world |
GetMovieGWorld(myMovie, &myMovGWorld, &myMovGDevice); |
if ((**myAppData).fUseOffscreenGWorld) { |
// we're using an offscreen graphics world, so set movie's GWorld to be that offscreen graphics world |
if (myMovGWorld != (**myAppData).fOffscreenGWorld) |
SetMovieGWorld(myMovie, (**myAppData).fOffscreenGWorld, GetGWorldDevice((**myAppData).fOffscreenGWorld)); |
} else { |
// we're not using an offscreen graphics world, so set movie GWorld to be the back buffer; |
if ((myMovGWorld != myBBufGWorld) || (myMovGDevice != myBBufGDevice)) |
SetMovieGWorld(myMovie, myBBufGWorld, myBBufGDevice); |
} |
////////// |
// |
// make sure the movie rectangle and movie matrix are set correctly |
// |
////////// |
if (myIsDrawing) { |
// if we weren't previously visible, make sure we are now |
GetMovieBox(myMovie, &myRect); |
if (EmptyRect(&myRect)) |
SetMovieBox(myMovie, &(**myAppData).fMovieBox); |
// when no offscreen GWorld is being used... |
if (!(**myAppData).fUseOffscreenGWorld) { |
// ...make sure the movie matrix is set correctly... |
if (!MacEqualRect(theRect, &(**myAppData).fPrevBBufRect)) { |
VRMoov_CalcImagingMatrix(myWindowObject, theRect); |
SetMovieMatrix(myMovie, &(**myAppData).fMovieMatrix); |
} |
// ...and, if we are compositing, force QuickTime to draw a movie frame when MoviesTask is called |
// (since the previous frame was automatically erased from the back buffer by QuickTime VR) |
if ((**myAppData).fCompositeMovie) |
UpdateMovie(myMovie); |
} |
} else { |
// if we're not visible, set the movie rectangle to an empty rectangle |
// so we're not wasting time trying to draw a movie frame |
MacSetRect(&myRect, 0, 0, 0, 0); |
SetMovieBox(myMovie, &myRect); |
} |
////////// |
// |
// draw a new movie frame into the movie's graphics world (and play movie sound) |
// |
////////// |
MoviesTask(myMovie, 1L); |
// MoviesTask(myMovie, 0); |
// if we got here, everything is okay so far |
myErr = noErr; |
////////// |
// |
// perform any additional compositing |
// |
////////// |
// that is, draw, using the current chroma key color, anything to be dropped out of the image; |
// note that this technique works *only* if we're using an offscreen graphics world |
if ((**myAppData).fUseOffscreenGWorld && (**myAppData).fCompositeMovie && myIsDrawing) { |
RGBColor myColor; |
// since we're using an offscreen graphics world, make sure we draw there |
SetGWorld((**myAppData).fOffscreenGWorld, GetGWorldDevice((**myAppData).fOffscreenGWorld)); |
// set up compositing environment |
GetForeColor(&myColor); |
RGBForeColor(&(**myAppData).fChromaColor); |
// do the drawing |
if ((**myAppData).fHideRegion != NULL) |
MacPaintRgn((**myAppData).fHideRegion); |
// restore original drawing environment |
RGBForeColor(&myColor); |
// restore original graphics world |
SetGWorld(myBBufGWorld, myBBufGDevice); |
} |
////////// |
// |
// if we're using an offscreen graphics world, copy it into the back buffer |
// |
////////// |
if (myIsDrawing) { |
if ((**myAppData).fUseOffscreenGWorld) { |
PixMapHandle myPixMap; |
// if anything relevant to DecompressSequenceFrameS has changed, reset the decompression sequence |
if ((myBBufGWorld != (**myAppData).fPrevBBufGWorld) || !(MacEqualRect(theRect, &(**myAppData).fPrevBBufRect))) { |
VRMoov_CalcImagingMatrix(myWindowObject, theRect); |
VRMoov_SetupDecompSeq(myWindowObject, myBBufGWorld); |
} |
myPixMap = GetGWorldPixMap((**myAppData).fOffscreenGWorld); |
LockPixels(myPixMap); |
// set the chroma key color, if necessary |
if ((**myAppData).fCompositeMovie) |
RGBBackColor(&(**myAppData).fChromaColor); |
// copy the image from the offscreen graphics world into the back buffer |
myErr = DecompressSequenceFrameS( (**myAppData).fImageSequence, |
#if TARGET_CPU_68K |
StripAddress(GetPixBaseAddr(myPixMap)), |
#else |
GetPixBaseAddr(myPixMap), |
#endif |
(**(**myAppData).fImageDesc).dataSize, |
0, |
NULL, |
NULL); |
// reset the chroma key color; |
// we need to do this because the buffer we just drew into might NOT actually |
// be the real back buffer (see Virtual Reality Programming With QuickTime VR, p. 1-154); |
// the copy between the intermediate buffer and the back buffer respects the current back color. |
if ((**myAppData).fCompositeMovie) |
RGBBackColor(&kWhiteColor); |
UnlockPixels(myPixMap); |
} |
} |
////////// |
// |
// finish up |
// |
////////// |
// keep track of the GWorld and rectangle passed to us this time |
(**myAppData).fPrevBBufGWorld = myBBufGWorld; |
(**myAppData).fPrevBBufRect = *theRect; |
// if we drew something, tell QuickTime VR |
if (myIsDrawing) |
*theFlagsOut = kQTVRBackBufferFlagDidDraw; |
bail: |
HUnlock((Handle)myAppData); |
return(myErr); |
} |
////////// |
// |
// VRMoov_GetEmbeddedMovieWidth |
// Get the width of the embedded movie. |
// |
////////// |
float VRMoov_GetEmbeddedMovieWidth (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData; |
float myWidth; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
myWidth = 0; |
else |
myWidth = (**myAppData).fMovieWidth; |
return(myWidth); |
} |
////////// |
// |
// VRMoov_SetEmbeddedMovieWidth |
// Set the width of the embedded movie. |
// |
////////// |
void VRMoov_SetEmbeddedMovieWidth (WindowObject theWindowObject, float theWidth) |
{ |
ApplicationDataHdl myAppData; |
QTVRInstance myInstance; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return; |
myInstance = (**theWindowObject).fInstance; |
if (myInstance == NULL) |
return; |
// install the desired width in our application data structure |
(**myAppData).fMovieWidth = theWidth; |
// clear out the existing area of interest |
QTVRRefreshBackBuffer(myInstance, 0); |
// reinstall the back buffer imaging procedure |
VRMoov_InstallBackBufferImagingProc(myInstance, theWindowObject); |
} |
////////// |
// |
// VRMoov_GetEmbeddedMovieCenter |
// Get the center of the embedded movie. |
// |
////////// |
void VRMoov_GetEmbeddedMovieCenter (WindowObject theWindowObject, QTVRFloatPoint *theCenter) |
{ |
ApplicationDataHdl myAppData; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) { |
theCenter->x = 0.0; |
theCenter->y = 0.0; |
} else { |
theCenter->x = (**myAppData).fMovieCenter.x; |
theCenter->y = (**myAppData).fMovieCenter.y; |
} |
} |
////////// |
// |
// VRMoov_SetEmbeddedMovieCenter |
// Set the center of the embedded movie. |
// |
////////// |
void VRMoov_SetEmbeddedMovieCenter (WindowObject theWindowObject, const QTVRFloatPoint *theCenter) |
{ |
ApplicationDataHdl myAppData; |
QTVRInstance myInstance; |
float myX, myY; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return; |
myInstance = (**theWindowObject).fInstance; |
if (myInstance == NULL) |
return; |
myX = theCenter->x; |
myY = theCenter->y; |
// subject the values passed in to the current view constraints |
QTVRWrapAndConstrain(myInstance, kQTVRPan, myX, &myX); |
QTVRWrapAndConstrain(myInstance, kQTVRTilt, myY, &myY); |
// install the desired center in our application data structure |
(**myAppData).fMovieCenter.x = myX; |
(**myAppData).fMovieCenter.y = myY; |
// clear out the existing area of interest |
QTVRRefreshBackBuffer(myInstance, 0); |
// reinstall the back buffer imaging procedure |
VRMoov_InstallBackBufferImagingProc(myInstance, theWindowObject); |
} |
////////// |
// |
// VRMoov_GetEmbeddedMovieScale |
// Get the scale of the embedded movie. |
// |
////////// |
float VRMoov_GetEmbeddedMovieScale (WindowObject theWindowObject) |
{ |
ApplicationDataHdl myAppData; |
float myScale; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
myScale = 0; |
else |
myScale = (**myAppData).fMovieScale; |
return(myScale); |
} |
////////// |
// |
// VRMoov_SetEmbeddedMovieScale |
// Set the scale factor of the embedded movie. |
// |
////////// |
void VRMoov_SetEmbeddedMovieScale (WindowObject theWindowObject, float theScale) |
{ |
ApplicationDataHdl myAppData; |
QTVRInstance myInstance; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return; |
myInstance = (**theWindowObject).fInstance; |
if (myInstance == NULL) |
return; |
// install the desired scale factor in our application data structure |
(**myAppData).fMovieScale = theScale; |
// clear out the existing area of interest |
QTVRRefreshBackBuffer(myInstance, 0); |
// reinstall the back buffer imaging procedure |
VRMoov_InstallBackBufferImagingProc(myInstance, theWindowObject); |
} |
////////// |
// |
// VRMoov_SetChromaColor |
// Set the chroma key color for a window object: |
// display color picker dialog and remember the newly-selected color. |
// |
////////// |
void VRMoov_SetChromaColor (WindowObject theWindowObject) |
{ |
#if TARGET_OS_MAC |
ColorPickerInfo myColorInfo; |
#endif |
#if TARGET_OS_WIN32 |
static CHOOSECOLOR myColorRec; |
static COLORREF myColorRef[16]; |
#endif |
ApplicationDataHdl myAppData; |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject); |
if (myAppData == NULL) |
return; |
#if TARGET_OS_MAC |
// pass in existing color |
myColorInfo.theColor.color.rgb.red = (**myAppData).fChromaColor.red; |
myColorInfo.theColor.color.rgb.green = (**myAppData).fChromaColor.green; |
myColorInfo.theColor.color.rgb.blue = (**myAppData).fChromaColor.blue; |
// not much here... |
myColorInfo.theColor.profile = 0L; |
myColorInfo.dstProfile = 0L; |
myColorInfo.flags = 0L; |
myColorInfo.placeWhere = kCenterOnMainScreen; |
myColorInfo.pickerType = 0L; |
myColorInfo.eventProc = gColorFilterUPP; |
myColorInfo.colorProc = NULL; |
myColorInfo.colorProcData = 0L; |
GetIndString(myColorInfo.prompt, kColorPickerTextStringID, 1); |
// set Edit menu info |
myColorInfo.mInfo.editMenuID = kEditMenuResID; |
myColorInfo.mInfo.undoItem = MENU_ITEM(IDM_EDITUNDO); |
myColorInfo.mInfo.cutItem = MENU_ITEM(IDM_EDITCUT); |
myColorInfo.mInfo.copyItem = MENU_ITEM(IDM_EDITCOPY); |
myColorInfo.mInfo.pasteItem = MENU_ITEM(IDM_EDITPASTE); |
myColorInfo.mInfo.clearItem = MENU_ITEM(IDM_EDITCLEAR); |
// call Color Picker |
if ((PickColor(&myColorInfo) == noErr) && (myColorInfo.newColorChosen)) { |
// install the newly chosen color in the palette |
(**myAppData).fChromaColor.red = myColorInfo.theColor.color.rgb.red; |
(**myAppData).fChromaColor.green = myColorInfo.theColor.color.rgb.green; |
(**myAppData).fChromaColor.blue = myColorInfo.theColor.color.rgb.blue; |
} |
#endif |
#if TARGET_OS_WIN32 |
myColorRec.lStructSize = sizeof(CHOOSECOLOR); |
myColorRec.hwndOwner = NULL; |
myColorRec.hInstance = NULL; |
VRMoov_MacRGBToWinRGB(&(**myAppData).fChromaColor, &(myColorRec.rgbResult)); |
myColorRec.lpCustColors = myColorRef; |
myColorRec.Flags = CC_RGBINIT | CC_FULLOPEN; |
myColorRec.lCustData = 0; |
myColorRec.lpfnHook = NULL; |
myColorRec.lpTemplateName = NULL; |
// call Common Color dialog |
if (ChooseColor(&myColorRec)) { |
// install the newly chosen color in the palette |
VRMoov_WinRGBToMacRGB(&(**myAppData).fChromaColor, myColorRec.rgbResult); |
} |
#endif |
} |
////////// |
// |
// VRMoov_ColorDialogEventFilter |
// Handle events before they get passed to the Color Picker dialog box. |
// |
////////// |
PASCAL_RTN Boolean VRMoov_ColorDialogEventFilter (EventRecord *theEvent) |
{ |
#if TARGET_OS_WIN32 |
#pragma unused(theEvent) |
#endif |
Boolean myEventHandled = false; |
OSErr myErr = noErr; |
#if TARGET_OS_MAC |
switch (theEvent->what) { |
case updateEvt: { |
if ((WindowPtr)theEvent->message != FrontWindow()) { |
QTFrame_HandleEvent(theEvent); |
myEventHandled = true; |
} |
break; |
} |
case nullEvent: { |
// do idle-time processing for all open windows in our window list |
WindowObject myWindowObject = NULL; |
ApplicationDataHdl myAppData = NULL; |
WindowReference myWindow = NULL; |
myWindow = QTFrame_GetFrontMovieWindow(); |
while (myWindow != NULL) { |
myWindowObject = (WindowObject)QTFrame_GetWindowObjectFromWindow(myWindow); |
myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindow(myWindow); |
if ((myWindowObject != NULL) && (myAppData != NULL)) |
if ((**myAppData).fMovie != NULL) |
QTVRUpdate((**myWindowObject).fInstance, kQTVRCurrentMode); |
myWindow = QTFrame_GetNextMovieWindow(myWindow); |
} |
myEventHandled = false; |
break; |
} |
case kHighLevelEvent: |
myErr = AEProcessAppleEvent(theEvent); |
if (myErr != noErr) |
myEventHandled = true; |
break; |
default: |
myEventHandled = false; |
break; |
} |
#endif |
return(myEventHandled); |
} |
////////// |
// |
// VRMoov_UncoverProc |
// The uncover function of the embedded movie. |
// |
////////// |
PASCAL_RTN OSErr VRMoov_UncoverProc (Movie theMovie, RgnHandle theRegion, long theRefCon) |
{ |
#pragma unused(theMovie, theRegion, theRefCon) |
return(noErr); |
} |
////////// |
// |
// VRMoov_SetVideoGraphicsMode |
// Set the video media graphics mode of the embedded movie. |
// |
////////// |
void VRMoov_SetVideoGraphicsMode (Movie theMovie, ApplicationDataHdl theAppData, Boolean theSetVGM) |
{ |
long myTrackCount; |
short myIndex; |
Track myTrack = NULL; |
Media myMedia = NULL; |
OSType myMediaType; |
if ((theMovie == NULL) || (theAppData == NULL)) |
return; |
myTrackCount = GetMovieTrackCount(theMovie); |
for (myIndex = 1; myIndex <= myTrackCount; myIndex++) { |
myTrack = GetMovieIndTrack(theMovie, myIndex); |
myMedia = GetTrackMedia(myTrack); |
GetMediaHandlerDescription(myMedia, &myMediaType, NULL, NULL); |
if (myMediaType == VideoMediaType) { |
if (theSetVGM) |
MediaSetGraphicsMode(GetMediaHandler(myMedia), srcCopy | transparent, &(**theAppData).fChromaColor); |
else |
MediaSetGraphicsMode(GetMediaHandler(myMedia), srcCopy, &kWhiteColor); |
} |
} |
return; |
} |
////////// |
// |
// VRMoov_GetVideoGraphicsPixelDepth |
// Return the highest pixel depth supported by a QuickTime movie. |
// |
////////// |
short VRMoov_GetVideoGraphicsPixelDepth (Movie theMovie) |
{ |
long myTrackCount; |
short myIndex; |
Track myMovieTrack = NULL; |
Media myMedia; |
OSType myMediaType; |
short myQuality; |
myTrackCount = GetMovieTrackCount(theMovie); |
for (myIndex = 1; myIndex <= myTrackCount; myIndex++) { |
myMovieTrack = GetMovieIndTrack(theMovie, myIndex); |
myMedia = GetTrackMedia(myMovieTrack); |
GetMediaHandlerDescription(myMedia, &myMediaType, NULL, NULL); |
if (myMediaType == VideoMediaType) { |
myQuality = GetMediaQuality(myMedia); |
if (myQuality >> 5) |
return(32); |
if (myQuality >> 4) |
return(16); |
if (myQuality >> 3) |
return(8); |
if (myQuality >> 2) |
return(4); |
if (myQuality >> 1) |
return(2); |
if (myQuality >> 0) |
return(1); |
} |
} |
return(0); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Color conversion utilities. |
// |
// Macintosh usually represents colors using an RGBColor structure, where each color component is a 16-bit |
// unsigned integer. Windows represents colors using a 32-bit unsigned COLORREF, where each color component |
// occupies 8 bits. The following two functions allow us to convert from Mac to Windows colors and back. We |
// need to do this because we use Mac-style colors when doing compositing. |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
#if TARGET_OS_WIN32 |
#define kMaxMacRGBValue 0xffff |
#define kMaxWinRGBValue 0x00ff |
#define MAC_TO_WIN_COMP(color) (color*((float)kMaxWinRGBValue/(float)kMaxMacRGBValue)) |
#define WIN_TO_MAC_COMP(color) (color*((float)kMaxMacRGBValue/(float)kMaxWinRGBValue)) |
////////// |
// |
// VRMoov_MacRGBToWinRGB |
// Convert an RGBColor structure into a COLORREF value. |
// |
////////// |
void VRMoov_MacRGBToWinRGB (RGBColorPtr theRGBColor, COLORREF *theColorRef) |
{ |
*theColorRef = RGB( (BYTE)MAC_TO_WIN_COMP(theRGBColor->red), |
(BYTE)MAC_TO_WIN_COMP(theRGBColor->green), |
(BYTE)MAC_TO_WIN_COMP(theRGBColor->blue)); |
} |
////////// |
// |
// VRMoov_WinRGBToMacRGB |
// Convert a COLORREF value into an RGBColor structure. |
// |
////////// |
void VRMoov_WinRGBToMacRGB (RGBColorPtr theRGBColor, COLORREF theColorRef) |
{ |
theRGBColor->red = (unsigned long)WIN_TO_MAC_COMP(GetRValue(theColorRef)); |
theRGBColor->green = (unsigned long)WIN_TO_MAC_COMP(GetGValue(theColorRef)); |
theRGBColor->blue = (unsigned long)WIN_TO_MAC_COMP(GetBValue(theColorRef)); |
} |
#endif // TARGET_OS_WIN32 |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14