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.
QDrawHandler.c
////////// |
// |
// File: QDrawHandler.c |
// |
// Contains: Code for creating a derived media handler component for QuickDraw pictures. |
// |
// Written by: Tim Monroe |
// Based on MyMediaComponent by John Wang (see develop issue 14), with some |
// assistance from SampleMediaHandler by deeje cooley. |
// |
// Copyright: © 1993-1999 by Apple Computer, Inc., all rights reserved. |
// |
// Change History (most recent first): |
// |
// <7> 02/25/99 rtm added some comments |
// <6> 01/28/99 rtm fixed bug that caused garbage to be drawn when Copy menu item selected |
// or a drag of movie clipping attempted |
// <5> 01/25/99 rtm got Windows DLL working |
// <4> 01/21/99 rtm got both MacOS PPC and 68K versions working |
// <3> 01/15/99 rtm coordinated with SampleMediaHandler by deeje cooley |
// <2> 01/05/99 rtm revised coding style |
// <1> 02/25/93 jw first file; based on MyComponent shell |
// |
// This project builds a derived (or custom) media handler. See the chapter "Derived Media Handler |
// Components" in the book Inside Macintosh: QuickTime Components for information about writing |
// derived media handler components. See also John Wang's article on derived media handlers in develop, |
// issue 14, for more information about the QuickDraw media handler. The main difference between his |
// code and the current sample code is that I've made the dispatching routines both PPC- and Windows-savvy, |
// using the ComponentDispatchHelper code. Also fixed a few bugs and added some more comments. |
// |
////////// |
////////// |
// |
// header files |
// |
////////// |
#include "QDrawHandler.h" |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Component dispatch helper defines |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
#if TARGET_CPU_68K |
#define COMPONENT_C_DISPATCHER |
#define COMPONENT_DISPATCH_MAIN |
#endif |
#define MEDIA_BASENAME() QDMH_ |
#define MEDIA_GLOBALS() QDMH_GlobalsHdl storage |
#define CALLCOMPONENT_BASENAME() MEDIA_BASENAME() |
#define CALLCOMPONENT_GLOBALS() MEDIA_GLOBALS() |
#define COMPONENT_UPP_PREFIX() uppMedia |
#define COMPONENT_SELECT_PREFIX() kMedia |
#define COMPONENT_DISPATCH_FILE "QDrawHandlerDispatch.h" |
#define GET_DELEGATE_COMPONENT() ((**storage).fDelegate) |
#include "Components.k.h" |
#include "MediaHandlers.k.h" |
#include "ComponentDispatchHelper.c" |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Required component calls |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QDMH_Open |
// Open the derived media handler component. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Open (QDMH_GlobalsHdl storage, ComponentInstance theSelf) |
{ |
#pragma unused(storage) |
QDMH_GlobalsHdl myStorage = NULL; |
ComponentInstance myComponent = NULL; |
// allocate the private global storage used by this component instance |
myStorage = (QDMH_GlobalsHdl)NewHandleClear(sizeof(QDMH_Globals)); |
if (myStorage == NULL) |
return(MemError()); |
SetComponentInstanceStorage(theSelf, (Handle)myStorage); |
// open the base media handler component and target it |
myComponent = OpenDefaultComponent(MediaHandlerType, BaseMediaType); |
if (myComponent == NULL) |
return(componentNotCaptured); |
ComponentSetTarget(myComponent, theSelf); |
(**myStorage).fDelegate = myComponent; |
(**myStorage).fSelf = theSelf; |
(**myStorage).fParent = theSelf; |
return(noErr); |
} |
////////// |
// |
// QDMH_Close |
// Close the derived media handler component. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Close (QDMH_GlobalsHdl storage, ComponentInstance theSelf) |
{ |
#pragma unused(theSelf) |
if (storage != NULL) { |
// close the base media handler component instance |
if ((**storage).fDelegate != NULL) |
CloseComponent((**storage).fDelegate); |
// dispose of the private global storage created by the _Open routine |
DisposeHandle((Handle)storage); |
} |
return(noErr); |
} |
////////// |
// |
// QDMH_Version |
// Return the version of the derived media handler component. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Version (QDMH_GlobalsHdl storage) |
{ |
#pragma unused(storage) |
return(kQDMH_Version); |
} |
////////// |
// |
// QDMH_Register |
// Register. |
// |
// This routine is called once (usually at boot time) when the Component Manager first |
// registers this component. Note that the cmpWantsRegisterMessage bit must be set in |
// the component flags of the component in order for this routine to be called. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Register (QDMH_GlobalsHdl storage) |
{ |
if (storage != NULL) |
return(noErr); // globals properly set up: the base media handler was targeted OK |
else |
return(-1); // globals not properly set up: don't register |
} |
////////// |
// |
// QDMH_Target |
// Target. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Target (QDMH_GlobalsHdl storage, ComponentInstance theTarget) |
{ |
// remember who is at the top of our calling chain |
(**storage).fParent = theTarget; |
// inform the base media handler of the change |
ComponentSetTarget((**storage).fDelegate, theTarget); |
return(noErr); |
} |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
// |
// Derived media handler functions |
// |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// |
////////// |
// |
// QDMH_Initialize |
// Initialize our media handler. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Initialize (QDMH_GlobalsHdl storage, GetMovieCompleteParams *theGMC) |
{ |
ComponentResult myErr = noErr; |
long myFlags; |
if ((storage == NULL) || (theGMC == NULL)) |
return(paramErr); |
// set general characteristics |
(**storage).fMovie = theGMC->theMovie; |
(**storage).fTrack = theGMC->theTrack; |
(**storage).fMedia = theGMC->theMedia; |
(**storage).fCurMediaRate = theGMC->effectiveRate; |
MacSetRect(&((**storage).fGraphicsBox), 0, 0, (short)(theGMC->width >> 16), (short)(theGMC->height >> 16)); |
(**storage).fTrackMatrix = theGMC->trackMovieMatrix; |
(**storage).fPort = theGMC->moviePort; |
(**storage).fDevice = theGMC->movieGD; |
(**storage).fSampleDescIndex = -1; |
// set media globals |
(**storage).fWhatChanged = kQDMHAllChanged; |
(**storage).fEnabled = false; |
(**storage).fNewMediaRate = theGMC->effectiveRate; |
(**storage).fPrevMediaTime = -1; |
// inform the base media handler of our capabilities: |
// * handlerHasSpatial indicates that we draw |
// * handlerNeedsBuffer indicates that we want QuickTime to maintain a drawing buffer for us |
myFlags = handlerHasSpatial | handlerNeedsBuffer; |
myErr = MediaSetHandlerCapabilities((**storage).fDelegate, myFlags, -1); |
return(myErr); |
} |
////////// |
// |
// QDMH_Idle |
// Draw our media sample. |
// |
// Here is where we get time to process the current media sample data. In our case, we want to extract |
// the QuickDraw picture data from the media and draw it into the current graphics port. If the sample |
// data consisted of a complete QuickDraw picture, we could get that picture by calling GetMediaSample |
// and then draw the picture by calling MacSetRect, TransformRect, and DrawPicture. (This is the route |
// taken in the derived media handler described in Inside Macintosh: QuickTime Components, pp. 10-9 to |
// 10-14.) Our media handler, however, supports key frames (also called sync frames), which complicate |
// the work we need to do here. A key frame is a sample that does not rely on previous samples for any |
// of its information. A non-key frame (or difference frame) contains just data that has changed from |
// the previous frame. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_Idle (QDMH_GlobalsHdl storage, TimeValue theMediaTime, long theFlagsIn, long *theFlagsOut, const TimeRecord *theMovieTime) |
{ |
#pragma unused(theMovieTime) |
Media myMedia = (**storage).fMedia; |
TimeValue myPrevMediaTime = (**storage).fPrevMediaTime; |
long myWhatChanged; // flags indicating changes in media environment |
GDHandle mySavedGD; |
CGrafPtr mySavedPort; |
Rect myDrawRect; |
Boolean myRedraw; // do we need to draw anything? |
Boolean myIsDone; |
TimeValue myTime; |
long mySize; |
PicHandle mySyncPic = NULL; |
PicHandle myCurrPic = NULL; |
TimeValue mySyncSampleTime; |
TimeValue myCurrSampleTime; |
long myCurrSampleIndex; |
OSErr myErr = noErr; |
////////// |
// |
// inspect theFlagsIn to determine what the Movie Toolbox wants us to do |
// |
////////// |
// don't draw anything if mPreflightDraw flag is set |
if (theFlagsIn & mPreflightDraw) |
return(myErr); |
////////// |
// |
// initialize |
// |
////////// |
// allocate space for two pictures (the current sample's picture and the previous sync frame); |
// GetMediaSample resizes these handles as necessary when returning the media data |
mySyncPic = (PicHandle)NewHandle(sizeof(Picture)); |
myCurrPic = (PicHandle)NewHandle(sizeof(Picture)); |
if ((mySyncPic == NULL) || (myCurrPic == NULL)) { |
myErr = memFullErr; |
goto bail; |
} |
GetGWorld(&mySavedPort, &mySavedGD); |
SetGWorld((GWorldPtr)(**storage).fPort, (**storage).fDevice); |
////////// |
// |
// get information about the current sample |
// |
////////// |
myErr = GetMediaSample(myMedia, (Handle)myCurrPic, 0, NULL, theMediaTime, &myCurrSampleTime, NULL, NULL, &myCurrSampleIndex, 0, NULL, NULL); |
if (myErr != noErr) |
goto bail; |
// check to see whether the sample description index has changed since the last sample |
if (myCurrSampleIndex != (**storage).fSampleDescIndex) { |
(**storage).fSampleDescIndex = myCurrSampleIndex; |
(**storage).fWhatChanged |= kQDMHSampleDescChanged; |
} |
////////// |
// |
// determine what, if anything, in the media has changed |
// (we don't need to draw the current media sample if nothing has changed) |
// |
////////// |
myRedraw = false; |
myWhatChanged = (**storage).fWhatChanged; |
if (myWhatChanged != kQDMHNothingChanged) { |
if (myWhatChanged & kQDMHSetActive) // if media was just enabled, then redraw; else don't change |
myRedraw = (**storage).fEnabled; |
if (myWhatChanged & kQDMHSetRate) { |
// if we are now playing in the opposite direction, then redraw |
if ((((**storage).fCurMediaRate < 0) && ((**storage).fNewMediaRate > 0)) |
|| (((**storage).fCurMediaRate > 0) && ((**storage).fNewMediaRate < 0))) |
myRedraw = true; |
(**storage).fCurMediaRate = (**storage).fNewMediaRate; |
} |
if (myWhatChanged & kQDMHTrackEdited) |
myRedraw = true; // if track edited, then redraw |
if (myWhatChanged & kQDMHSetGWorld) |
myRedraw = true; // if new GWorld, then redraw |
if (myWhatChanged & kQDMHSetDimensions) |
myRedraw = true; // if new dimensions, then redraw |
if (myWhatChanged & kQDMHSetMatrix) |
myRedraw = true; // if new matrix, then redraw |
if (myWhatChanged & kQDMHSampleDescChanged) { |
QDrawDescriptionHandle myQDDesc; |
// the sample description has changed; make sure it's a version we can handle |
myQDDesc = (QDrawDescriptionHandle)NewHandleClear(sizeof(QDrawDescription)); |
if (myQDDesc != NULL) { |
GetMediaSampleDescription(myMedia, (**storage).fSampleDescIndex, (SampleDescriptionHandle)myQDDesc); |
#if HANDLER_SWAPS_SAMPLE_DESC |
if ((**myQDDesc).version > kQDMH_Version) |
#else |
if ((**myQDDesc).version > EndianU32_NtoB(kQDMH_Version)) |
#endif |
(**storage).fEnabled = false; |
DisposeHandle((Handle)myQDDesc); |
} else { |
(**storage).fEnabled = false; |
} |
} |
// clear out flags indicating changes in media environment |
(**storage).fWhatChanged = kQDMHNothingChanged; |
} |
// if we are playing the movie backwards, we must always redraw from last sync frame |
if ((**storage).fCurMediaRate < 0) |
myRedraw = true; |
////////// |
// |
// we do not need to draw anything if myRedraw is false and if myPrevMediaTime == myCurrSampleTime |
// (since we've already drawn that sample and nothing has occurred to cause us to have to redraw it) |
// |
// otherwise, we do need to draw; what we draw depends on the value of myRedraw: |
// (a) if myRedraw = true, redraw everything since the previous key frame |
// (b) if myRedraw = false, redraw everything since the previous key frame or the previous media frame, |
// whichever is closer to current frame |
// |
////////// |
if (myRedraw || (myPrevMediaTime != myCurrSampleTime)) { |
// find the previous key frame; note that we use the nextTimeEdgeOK flag, since the current frame |
// might be a key frame; note also that if the media doesn't contain any key frames, the sample |
// time returned in mySyncSampleTime is the same as myCurrSampleTime (that is, every frame is a key |
// frame) |
GetMediaNextInterestingTime(myMedia, nextTimeSyncSample + nextTimeEdgeOK, myCurrSampleTime, -1, &mySyncSampleTime, NULL); |
// if myRedraw = false, mySyncSampleTime <= myPrevMediaTime, and myCurrSampleTime is ahead of |
// myPrevMediaTime, then search to set the place to draw as the sample after myPrevMediaTime |
if (!myRedraw && (mySyncSampleTime <= myPrevMediaTime) && (myPrevMediaTime < myCurrSampleTime)) { |
myTime = mySyncSampleTime; |
while ((myTime >= 0) && (myTime < myPrevMediaTime)) |
GetMediaNextInterestingTime(myMedia, nextTimeMediaSample, myTime, 1, &myTime, NULL); |
if ((myTime == myPrevMediaTime) && (myTime != -1)) { |
GetMediaNextInterestingTime(myMedia, nextTimeMediaSample, myTime, 1, &myTime, NULL); |
if (myTime != -1) |
mySyncSampleTime = myTime; |
} |
} |
////////// |
// |
// draw the picture, beginning at mySyncSampleTime; but don't draw if the media is disabled |
// |
////////// |
myIsDone = false; |
myTime = mySyncSampleTime; |
while (!myIsDone && (**storage).fEnabled) { |
if (myTime == myCurrSampleTime) |
myIsDone = true; |
myErr = GetMediaSample(myMedia, (Handle)mySyncPic, 0, &mySize, myTime, NULL, NULL, NULL, NULL, 0, NULL, NULL); |
if (myErr != noErr) |
goto bail; |
myDrawRect = (**storage).fGraphicsBox; |
TransformRect(&(**storage).fTrackMatrix, &myDrawRect, NULL); |
DrawPicture(mySyncPic, &myDrawRect); |
if (!myIsDone) { |
GetMediaNextInterestingTime(myMedia, nextTimeMediaSample, myTime, 1, &myTime, NULL); |
if (myTime < 0) |
myIsDone = true; |
} |
} |
// say we drew somthing |
*theFlagsOut |= mDidDraw; |
} |
// update the previous media time |
(**storage).fPrevMediaTime = myCurrSampleTime; |
////////// |
// |
// clean up and return |
// |
////////// |
bail: |
SetGWorld((GWorldPtr)mySavedPort, mySavedGD); |
if (mySyncPic != NULL) |
DisposeHandle((Handle)mySyncPic); |
if (myCurrPic != NULL) |
DisposeHandle((Handle)myCurrPic); |
return(myErr); |
} |
////////// |
// |
// QDMH_SetActive |
// Set the enabled state of the media. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SetActive (QDMH_GlobalsHdl storage, Boolean theEnableMedia) |
{ |
if ((**storage).fEnabled != theEnableMedia) { |
(**storage).fEnabled = theEnableMedia; |
(**storage).fWhatChanged |= kQDMHSetActive; |
} |
return(noErr); |
} |
////////// |
// |
// QDMH_SetRate |
// Set the media rate. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SetRate (QDMH_GlobalsHdl storage, Fixed theRate) |
{ |
// save the new rate in fNewMediaRate so that we can compare with previous rate; |
// if the new rate is in the same direction, we won't want to redraw again from the previous key frame |
if ((**storage).fNewMediaRate != theRate) { |
(**storage).fNewMediaRate = theRate; |
(**storage).fWhatChanged |= kQDMHSetRate; |
} |
return(noErr); |
} |
////////// |
// |
// QDMH_TrackEdited |
// Set the track-edited state. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_TrackEdited (QDMH_GlobalsHdl storage) |
{ |
(**storage).fWhatChanged |= kQDMHTrackEdited; |
return(noErr); |
} |
////////// |
// |
// QDMH_SetGWorld |
// Set the media graphics port. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SetGWorld (QDMH_GlobalsHdl storage, CGrafPtr thePort, GDHandle theGD) |
{ |
(**storage).fPort = thePort; |
(**storage).fDevice = theGD; |
(**storage).fWhatChanged |= kQDMHSetGWorld; |
return(noErr); |
} |
////////// |
// |
// QDMH_SetDimensions |
// Set the media dimensions. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SetDimensions (QDMH_GlobalsHdl storage, Fixed theWidth, Fixed theHeight) |
{ |
MacSetRect(&((**storage).fGraphicsBox), 0, 0, (short)(theWidth >> 16), (short)(theHeight >> 16)); |
(**storage).fWhatChanged |= kQDMHSetDimensions; |
return(noErr); |
} |
////////// |
// |
// QDMH_SetMatrix |
// Set the track or movie matrix. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SetMatrix (QDMH_GlobalsHdl storage, MatrixRecord *theTrackMovieMatrix) |
{ |
// don't cause unnecessary updates if the matrix doesn't really change |
// (this can happen if the resize button is clicked on, but not moved) |
if (!EqualMatrix(&((**storage).fTrackMatrix), theTrackMovieMatrix)) { |
(**storage).fTrackMatrix = *theTrackMovieMatrix; |
(**storage).fWhatChanged |= kQDMHSetMatrix; |
} |
return(noErr); |
} |
////////// |
// |
// QDMH_SampleDescriptionChanged |
// Handle changes to the sample description tables. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SampleDescriptionChanged (QDMH_GlobalsHdl storage, long theIndex) |
{ |
// the sample description tables store info such as data version |
(**storage).fSampleDescIndex = theIndex; |
(**storage).fWhatChanged |= kQDMHSampleDescChanged; |
return(noErr); |
} |
#if HANDLER_SWAPS_SAMPLE_DESC |
////////// |
// |
// QDMH_SampleDescriptionB2N |
// Convert our sample description from big- to native-endian format. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SampleDescriptionB2N (QDMH_GlobalsHdl storage, SampleDescriptionHandle theSampleDesc) |
{ |
#pragma unused(storage) |
QDrawDescriptionHandle myQDDesc = (QDrawDescriptionHandle)theSampleDesc; |
if ((myQDDesc == NULL) || (*myQDDesc == NULL)) |
return(paramErr); |
// flip any fields in our sample description that are not in the SampleDescription structure |
(**myQDDesc).version = EndianU32_BtoN((**myQDDesc).version); |
return(noErr); |
} |
////////// |
// |
// QDMH_SampleDescriptionN2B |
// Convert our sample description from native- to big-endian format. |
// |
////////// |
PASCAL_RTN ComponentResult QDMH_SampleDescriptionN2B (QDMH_GlobalsHdl storage, SampleDescriptionHandle theSampleDesc) |
{ |
#pragma unused(storage) |
QDrawDescriptionHandle myQDDesc = (QDrawDescriptionHandle)theSampleDesc; |
if ((myQDDesc == NULL) || (*myQDDesc == NULL)) |
return(paramErr); |
// flip any fields in our sample description that are not in the SampleDescription structure |
(**myQDDesc).version = EndianU32_NtoB((**myQDDesc).version); |
return(noErr); |
} |
#endif // HANDLER_SWAPS_SAMPLE_DESC |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14