/***************************************************************************** CreateTransparentMovie -- Create a Movie with Transparent Images of IceFlow When this program runs, its only output is to create the quicktime movie. When you play the movie in the QT player, the image begins as pure white. Then as the movie progresses, Ice Flow gradually appears until he is all the way present at the end. Define the Movie: Create empty movie file and in-memory movie structures Step 1 - movie Step 2 - track Step 3 - track media Populate the Movie: Step 4 - Add image data to movie file Close the Movie: Step 5 - Update in-memory movie structures Step 6 - Append in-memory movie structures to end of file ('moov' atom) Step 7 - Move 'moov' atom to front of file and interleave samples (optional) Step 8 - Close movie file and discard in-memory movie structures Environment: OS X version 10.1.5 Creation: open Project Builder click file menu -- New Project click "Standard Tool" at the end of the list name=InitQT, directory -- pick one ^click on InitQT -- new Group give it the name "Frameworks" ^click on Frameworks -- choose add frameworks choose QuickTime.framework -- reference style = default ^click on Frameworks -- choose add frameworks choose Carbon.framework -- reference style = default ^click on main.c under the "Source" group choose "Show Info" change Reference Style from "Group Relative" to "Project Relative" *****************************************************************************/ /* Include the Quicktime header. ============================= */ #include /* Define the function prototypes. =============================== */ OSErr add_video_samples_to_media (Media media, short sTrackWidth, short sTrackHeight, long lNumImages); static void draw_image (short sTrackWidth, short sTrackHeight, long lImageNumber, long lNumImages, GWorldPtr pMovieGWorld); /* M A I N * */ int main (int argc, char *argv[]) { OSErr osErr; short sResId; short sResRefNum = 0; FSSpec fsSpec; Movie movie = nil; Track track = NULL; Media media = NULL; Movie flatMovie = nil; char *pszFilename = "../my.mov"; char *pszFlatFilename = "../myflat.mov"; EnterMovies (); /* Initialize Quicktime */ /* Load the FSSpec structure to describe the receiving file. For a description of this and related calls see http://developer.apple.com/quicktime/icefloe/dispatch004.html. ================================================================ */ osErr = NativePathNameToFSSpec (pszFilename, &fsSpec, 0); if (osErr && (osErr != fnfErr)) /* File-not-found error is ok */ { printf ("NativePathNameToFSSpec failed %d\n", osErr); goto bail; } /* Step 1: Create a new, empty movie file and a movie that references that file (CreateMovieFile). ======================================================================== */ osErr = CreateMovieFile ( &fsSpec, /* FSSpec specifier */ FOUR_CHAR_CODE('TVOD'), /* file creator type, TVOD = QT player*/ smCurrentScript, /* movie script system to use */ createMovieFileDeleteCurFile /* movie file creation flags */ | createMovieFileDontCreateResFile, &sResRefNum, /* returned file ref num to data fork */ &movie /* returned handle to open empty movie*/ /* that references the created file */ ); if (osErr) { printf ("CreateMovieFile failed %d\n", osErr); goto bail; } /* Step 2: Add a new track to that movie (NewMovieTrack). ======================================================= */ track = NewMovieTrack ( movie, /* the movie to add track to */ ((long) 400 << 16), /* width of track in pixels (Fixed) */ FixRatio (320, 1), /* height of track in pixels (Fixed) */ kNoVolume /* default volume level */ ); osErr = GetMoviesError (); if (osErr) { printf ("NewMovieTrack failed %d\n", osErr); goto bail; } /* Step 3: Add a new media to that track (NewTrackMedia). ======================================================= */ media = NewTrackMedia ( track, /* the track to add the media to */ VideoMediaType, /* media type, e.g. SoundMediaType */ 600, /* num media time units that elapse/sec*/ NULL, /* ptr to file that holds media sampls*/ 0 /* type of ptr to media samples */ ); osErr = GetMoviesError (); if (osErr) { printf ("NewTrackMedia failed %d\n", osErr); goto bail; } /* Step 4: Add media samples to the media. ======================================== */ BeginMediaEdits (media); /* Inform the Movie Toolbox that we */ /* want to change the media samples */ /* referenced by a track's media. */ /* This opens the media container */ /* and makes it ready to receive */ /* and/or remove sample data. */ add_video_samples_to_media (media, 400, 320, 100); EndMediaEdits (media); /* Inform the Movie Toolbox that they */ /* can close the media container. */ /* Step 5: Insert a reference into the track that specifies which of the media samples to play and when to start playing them. ====================================================================== */ InsertMediaIntoTrack ( track, /* the track to update. */ 0, /* time in track where the specified */ /* media samples should start playg */ /* using movie time scale. */ 0, /* time in media samples of the first */ /* sample to play using media time */ /* scale. */ GetMediaDuration (media), /* duration of media samples to play */ /* using media time scale. */ fixed1 /* rate at which to play the samples. */ ); /* Step 6: Append the movie atom to the movie file (AddMovieResource). ==================================================================== */ sResId = movieInDataForkResID; osErr = AddMovieResource ( movie, /* movie to create moov atom from */ sResRefNum, /* file to receive the moov atom */ &sResId, /* id num of movie resource (res fork)*/ pszFilename /* name of movie resource (res fork) */ ); if (osErr) { printf ("AddMovieResource failed %d\n", osErr); goto bail; } /* Step 7 (optional): Place the movie atom as the first atom in a new movie file, and interleave the media data (FlattenMovieData). First close the movie file that CreateMovieFile opened and define the file into which the flattened movie should be written. ==================================================================== */ if (sResRefNum != 0) { CloseMovieFile (sResRefNum); sResRefNum = 0; } osErr = NativePathNameToFSSpec (pszFlatFilename, &fsSpec, 0); if (osErr && (osErr != fnfErr)) { printf ("NativePathNameToFSSpec 2 failed %d\n", osErr); goto bail; } flatMovie = FlattenMovieData ( movie, flattenAddMovieToDataFork | flattenForceMovieResourceBeforeMovieData | flattenCompressMovieResource, &fsSpec, FOUR_CHAR_CODE('TVOD'), smSystemScript, createMovieFileDeleteCurFile | createMovieFileDontCreateResFile ); osErr = GetMoviesError (); if (osErr) { printf ("FlattenMovieData failed %d\n", osErr); goto bail; } /* Step 8: Close the movie file that CreateMovieFile opened (if necessary) and dispose of the movie memory structures (DisposeMovie). ======================================================================== */ bail: if (sResRefNum != 0) CloseMovieFile (sResRefNum); if (movie != NULL) DisposeMovie (movie); if (flatMovie != NULL) DisposeMovie (flatMovie); ExitMovies (); /* Finalize Quicktime */ return (0); } /* A D D _ V I D E O _ S A M P L E S _ T O _ M E D I A * * * */ OSErr add_video_samples_to_media (Media media, short sTrackWidth, short sTrackHeight, long lNumImages) { OSErr osErr; Rect rect; long lImageNumber; CodecType codecType; GWorldPtr pMovieGWorld = NULL; PixMapHandle pixMapHandle = NULL; Ptr pCompressedData = NULL; CGrafPtr pSavedPort = NULL; GDHandle hSavedDevice = NULL; Handle hCompressedData = NULL; long lMaxCompressionSize = 0L; ImageDescriptionHandle hImageDescription = NULL; /* Create a new offscreen graphics world that will hold the movie's drawing surface. draw_image() copies the image of IceFlow to this surface with varying amounts of transparency. ================================================================= */ MacSetRect (&rect, 0, 0, sTrackWidth, sTrackHeight); osErr = NewGWorld ( &pMovieGWorld, /* receives the new GWorld. */ 24, /* pixel depth in bits/pixel */ &rect, /* desired size of the GWorld. */ NULL, NULL, (GWorldFlags) 0 ); if (osErr != noErr) { printf ("NewGWorld 1 failed %d\n", osErr); goto bail; } /* Retrieve the pixel map associated with that graphics world and lock the pixel map in memory. GetMaxCompressionSize() and CompressImage() only operate on pixel maps, not graphics worlds. ===================================================================== */ pixMapHandle = GetGWorldPixMap (pMovieGWorld); if (pixMapHandle == NULL) { printf ("GetGWorldPixMap failed\n"); goto bail; } LockPixels (pixMapHandle); /* Get the maximum number of bytes required to hold an image having the specified characteristics compressed using the specified compressor. ==================================================================== */ codecType = kJPEGCodecType; osErr = GetMaxCompressionSize ( pixMapHandle, /* the pixel map to compress from. */ &rect, /* the image rectangle. */ 0, /* let ICM choose image bit depth. */ codecNormalQuality, /* compression quality specifier. */ codecType, /* desired compression type */ (CompressorComponent) anyCodec, /* codec specifier. */ &lMaxCompressionSize /* receives max bytes needed for cmp. */ ); if (osErr != noErr) { printf ("GetMaxCompressionSize failed %d\n", osErr); goto bail; } /* Allocate a buffer to hold the compressed image data by creating a new handle. ===================================================================== */ hCompressedData = NewHandle (lMaxCompressionSize); if (hCompressedData == NULL) { printf ("NewHandle(%ld) failed\n", lMaxCompressionSize); goto bail; } /* Lock the handle and then dereference it to obtain a pointer to the data buffer because CompressImage() wants us to pass it a pointer, not a handle. ======================================================================= */ HLockHi (hCompressedData); pCompressedData = *hCompressedData; /* Create an image description object in memory of minimum size to pass to CompressImage(). CompressImage() will resize the memory as necessary so create it small here. ==================================================================== */ hImageDescription = (ImageDescriptionHandle) NewHandle (4); if (hImageDescription == NULL) { printf ("NewHandle(4) failed\n"); goto bail; } /* Save the current GWorld and set the offscreen GWorld as current. ================================================================ */ GetGWorld (&pSavedPort, &hSavedDevice); SetGWorld (pMovieGWorld, NULL); /* Each cycle of this loop draws one image into the GWorld, compresses it, then adds it to the movie. =================================================================== */ for (lImageNumber = 1; lImageNumber <= lNumImages; lImageNumber++) { /* Clear the rectangle then draw the image using the specified degree of opacity onto the rectangle. =========================================================== */ EraseRect (&rect); draw_image (sTrackWidth, sTrackHeight, lImageNumber, lNumImages, pMovieGWorld); /* Compress the pixel map that has just been drawn on. Also resize and fill in the image description. Resulting image size can be discovered by consulting the image description field dataSize. ================================================================ */ osErr = CompressImage ( pixMapHandle, /* the pixel map of the offscreen img */ &rect, /* portion of the image to compress */ codecNormalQuality, /* same compression quality as above */ codecType, /* same codec specifier as above */ hImageDescription, /* the created image description. */ pCompressedData /* ptr to bufr that receives cmp image*/ ); if (osErr != noErr) { printf ("CompressImage failed %d\n", osErr); goto bail; } /* Add the compressed image to the movie. ====================================== */ osErr = AddMediaSample ( media, /* the media to add the image to. */ hCompressedData, /* the compressed image to add. */ 0, /* byte offs into data to begin readg */ (**hImageDescription).dataSize,/* num bytes to be copied into media. */ 600 / 10, /* duration of the frame (media time) */ (SampleDescriptionHandle) hImageDescription, /* image desc cast to */ /* a sample description since both */ /* both structures start with same */ /* fields. */ 1, /* num samples in the data buffer. */ 0, /* default flags */ NULL /* ptr to receive media time in which */ /* the image was added. */ ); if (osErr != noErr) { printf ("AddMediaSample failed %d\n", osErr); goto bail; } } bail: SetGWorld (pSavedPort, hSavedDevice); if (hImageDescription != NULL) DisposeHandle ((Handle) hImageDescription); if (hCompressedData != NULL) DisposeHandle (hCompressedData); if (pMovieGWorld != NULL) DisposeGWorld (pMovieGWorld); return (osErr); } /* D R A W _ I M A G E * * Draws Ice Flow once onto an imageGWorld, then copies a transparent * version of Ice Flow's picture to the passed movieGWorld. The amount of * transparency depends upon the passed image number (1st image is 0%, * last image is 100%). * */ static void draw_image (short sTrackWidth, short sTrackHeight, long lImageNumber, long lNumImages, GWorldPtr pMovieGWorld) { OSErr osErr; ComponentResult componentResult; Rect rect; FSSpec fsSpec; RGBColor rgbColor; Handle handle = NULL; char *pszImageFilename = "../../IceFlow.png"; static GWorldPtr pImageGWorld = NULL; static GraphicsImportComponent graphicsImportComponent = NULL; MacSetRect (&rect, 0, 0, sTrackWidth, sTrackHeight); /* If the picture (and its importer) has not been created yet, then create it. ================================================================ */ if (graphicsImportComponent == NULL) { componentResult = NewGWorld ( &pImageGWorld, /* returns the new GWorld. */ 24, /* pixel depth in bits/pixel */ &rect, /* width & height to make the GWorld */ NULL, /* */ NULL, /* */ (GWorldFlags) 0 /* */ ); if (componentResult != noErr) { printf ("NewGWorld 2 failed %ld\n", componentResult); goto bail; } /* Describe the file that holds the picture of Ice Flow. ===================================================== */ osErr = NativePathNameToFSSpec (pszImageFilename, &fsSpec, 0); if (osErr) { printf ("NativePathNameToFSSpec 2 failed %d\n", osErr); goto bail; } /* Locate and open a graphics importer component which can be used to draw Ice Flow's file. If a suitable importer is not found the ComponentInstance is set to NULL. ================================================================== */ osErr = GetGraphicsImporterForFile ( &fsSpec, /* the file to be imported */ &graphicsImportComponent /* returned GraphicsImporterComponent */ ); if (osErr) { printf ("GetGraphicsImporterForFile failed %d\n", osErr); goto bail; } /* Tell the graphics importer what GWorld drawing surface to use. ============================================================== */ componentResult = GraphicsImportSetGWorld ( graphicsImportComponent, pImageGWorld, NULL ); if (componentResult != noErr) { printf ("GraphicsImportSetGWorld failed %ld\n", componentResult); goto bail; } /* Ask the graphics importer to draw its picture onto the supplied drawing surface. =============================================================== */ componentResult = GraphicsImportDraw (graphicsImportComponent); if (componentResult != noErr) { printf ("GraphicsImportDraw failed %ld\n", componentResult); goto bail; } } /* Set the blend amount (0 = fully transparent; 0xffff = fully opaque). ==================================================================== */ rgbColor.red = (0xffff / lNumImages - 1) * (lImageNumber - 1); rgbColor.green = (0xffff / lNumImages - 1) * (lImageNumber - 1); rgbColor.blue = (0xffff / lNumImages - 1) * (lImageNumber - 1); OpColor (&rgbColor); /* Blend the picture (in the source GWorld) into the empty rectangle (in the destination GWorld). ================================================================= */ CopyBits ( (BitMapPtr) *GetGWorldPixMap (pImageGWorld), (BitMapPtr) *GetGWorldPixMap (pMovieGWorld), &rect, &rect, blend, NULL ); if (lImageNumber == lNumImages) goto bail; return; bail: if (handle != NULL) DisposeHandle (handle); if (graphicsImportComponent != NULL) CloseComponent (graphicsImportComponent); } /*============================( End of Source )============================*/