EffectFramework/Effect.c

/*
    File:       Effect.c
    
    Description: QuickTime effect component framework
 
    Author:     QuickTime Engineering
 
    Copyright:  © Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
    
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                
    Change History (most recent first):
       <1>      05/11/02    era     updated for X
*/
 
/*
    Dimmer2 Effect - a sample QuickTime video effect.
    
    This effect uses two sources as input, and renders that source with
    a dim value that starts at full on, and ramps to full off. This isn't very
    useful as a real effect, but shows instead how to create an effect.
    
    READ THIS PARAGRAPH BEFORE DOING ANYTHING ELSE IN THIS FILE:
    To find things to change to implement your effect, seach for *** CHANGE ***.  
    Code that will require modification is bracketted by CHANGE/END CHANGE.
    
    There are eleven places to change code, and one place where you write new
    code that implements your actual effect.  MAKE SURE YOU LOOK AT ALL OF THEM.
    
    *IMPORTANT*
    
    You MUST also ensure that the Effect.r file is kept in sync with this code.
    In particular it is very important that you update the 'atms' resource
    description so that the parameters of your effect are correctly described.
    Full details of the format of the 'atms' resource can be found in the Effects
    chapter of the QuickTime documentation
    
    ALSO PLEASE NOTE THAT THE LINK WARNING ABOUT THE COMPONENT DISPATCH ENTRY POINT
    NOT BEING A ROUTINE DESCRIPTOR IS NORMAL AND CAN BE IGNORED WHEN BUILDING THE PPC
    TARGET.
    
    written by Tom Dowdy and Dan Crow
    © Copyright 1997-2002 Apple Computer, Inc. All rights reserved.
*/
 
// --------------------------------------------------------------------------------------
// INCLUDES
// --------------------------------------------------------------------------------------
 
#if (__APPLE_CC__ || __MACH__)
    #include <Carbon/Carbon.h>
    #include <QuickTime/QuickTime.h>
#elif TARGET_API_MAC_CARBON
    #include <Carbon.h>
    #include <QuickTime.h>
#else
    #include <ConditionalMacros.h>
    #include <ImageCodec.h>
    #include <QuickDraw.h>
#endif
 
#include "EffectDefinitions.h"
 
/*#if PRAGMA_STRUCT_ALIGN
    #pragma options align=mac68k
#endif*/
 
#include "EffectUtilities.h"
 
// --------------------------------------------------------------------------------------
// INTERNAL DEFINES
// --------------------------------------------------------------------------------------
 
// *** CHANGE *** This defines the number of frames that can be queued for
// asynchronous rendering by this effect.  The value 0 indicates the effect
// runs synchronously.
#define kMaxAsyncFrames     0
// *** END CHANGE ***
 
// *** CHANGE *** Change if your effect accepts multiple sources.  This value
// is the maximum number of sources this effect operates on.
#define kMaxSources         2
// *** END CHANGE ***
 
// --------------------------------------------------------------------------------------
// INTERNAL TYPEDEFS
// --------------------------------------------------------------------------------------
 
// Structure used to store information about each source
typedef struct {                        
    CDSequenceDataSourcePtr src;
    void                    *srcBaseAddr;
    long                    srcRowBytes;
} SourceRecord;
 
// This is the structure used to store information for drawing a single frame of the effect
typedef struct {                            
    SourceRecord        sources[kMaxSources];   // inputs
 
    void                *dstBaseAddr;           // output base address
    long                dstRowBytes;            // output row bytes
    long                height;                 // output height
    long                width;                  // output width
    short               destDepth;              // output depth
    OSType              dstPixelFormat;         // output pixel format
    
    // *** CHANGE *** Here is where we store values relating to a single frame
    // of the effect.  These are the parameters to the effect, modified by the
    // percentage into the effect which we are.
    
    long                direction;
    short               dimValue;               // dimming value, from 0 to 255
    
    // *** END CHANGE ***
} BlitGlobals;
 
// global data per instance. This holds data for the entire effect as it runs its course.
typedef struct {
    ComponentInstance   self;       // ourselves
    ComponentInstance   target;     // top of the calling chain
    ComponentInstance   delegate;   // if we can't handle an effect, this one can
    
    BlitGlobals         blitter;    // information for drawing the data
    OSType              **wantedDestinationPixelTypeH;
    
    #if kMaxAsyncFrames > 0
        volatile short  asyncCount; // number of outstanding frames we have     
    #endif
    
    // parameter/source/dest seed tracking
    long                        initialized;
    long                        frameNumber;            
    long                        virtualDuration;
    long                        majorSourceChangeSeed;
    
    TweenGlobals                tweenGlobals;
 
    // *** CHANGE *** PLACE PARAMETERS FOR YOUR EFFECT HERE
    
    long                        direction;  // direction of blit.  0 for dim to bright, 255 for bright to dim
        
    TweenContainerRecord        percentage;
    
    // *** END CHANGE ***
} EffectGlobals;
 
// --------------------------------------------------------------------------------------
// DISPATCHER
// --------------------------------------------------------------------------------------
/************************************************************************************ 
 *  This is the main dispatcher for our codec. All calls from the codec manager
 *  will come through here, with a unique selector and corresponding parameter block.
 *
 *  This routine must be first in the code segment of the codec component.
 *
 *  We use the normal dispatcher rather than the codec dispatcher as we need to
 *  implement the extra effects routines on top of the codec ones.
 */
/************************************************************************************/
// Begin Dispatch Stuff
 
// Used by Component Dispatch Helper to call our routines
#define CALLCOMPONENT_BASENAME()        EffectsFrame
#define CALLCOMPONENT_GLOBALS()         EffectGlobals * storage
 
// Used by Type's .k.h to create prototypes for our routines
#define IMAGECODEC_BASENAME()           CALLCOMPONENT_BASENAME()
#define IMAGECODEC_GLOBALS()            CALLCOMPONENT_GLOBALS()
 
// Used by SubType's .k.h to create prototypes for our routines
#define IMAGECODECEFFECT_BASENAME()     CALLCOMPONENT_BASENAME()
#define IMAGECODECEFFECT_GLOBALS()      CALLCOMPONENT_GLOBALS()
 
// Other defines for Component Dispatch Helper
#define COMPONENT_DISPATCH_FILE         "EffectDispatch.h"  // describes what to dispatch
#define GET_DELEGATE_COMPONENT()        (storage->delegate) // how to find delegate component
 
#define COMPONENT_UPP_SELECT_ROOT()     ImageCodec          // root for Type's UPP_PREFIX and SELECT_PREFIX     
 
#include "Components.k.h"               // StdComponent's .k.h
#include "ImageCodec.k.h"               // Type's .k.h
#include "ComponentDispatchHelper.c"    // make our dispatcher and cando
 
// End Dispatch Stuff
/************************************************************************************/
 
// --------------------------------------------------------------------------------------
// EFFECT CODE - MULTIPLE BIT-DEPTH AND PIXEL FORMATS
//      For clarity and reuse sake, the drawing code is being kept separate from the
//      component interface code.
// --------------------------------------------------------------------------------------
 
// 16BE555 - k16BE555PixelFormat /* 16 bit BE rgb 555 (Mac)*/
#define EffectFilter16 EffectFilter16BE555
#define srcIs16BE555 1
#define dstIs16BE555 1
#include "EffectFilter16.c"
#undef EffectFilter16
#undef srcIs16BE555
#undef dstIs16BE555
 
#if NON_MAC_PIXEL_FORMATS
// 16LE555 - k16LE555PixelFormat /* 16 bit LE rgb 555 (PC)*/
#define EffectFilter16 EffectFilter16LE555
#define srcIs16LE555 1
#define dstIs16LE555 1
#include "EffectFilter16.c"
#undef EffectFilter16
#undef srcIs16LE555
#undef dstIs16LE555
 
// 16LE565 - k16LE565PixelFormat  /* 16 bit LE rgb 565*/
#define EffectFilter16 EffectFilter16LE565
#define srcIs16LE565 1
#define dstIs16LE565 1
#include "EffectFilter16.c"
#undef EffectFilter16
#undef srcIs16LE565
#undef dstIs16LE565
#endif
 
// 32ARGB - k32ARGBPixelFormat /* 32 bit argb    (Mac)*/
#define EffectFilter32 EffectFilter32ARGB
#define srcIs32ARGB 1
#define dstIs32ARGB 1
#include "EffectFilter32.c"
#undef EffectFilter32
#undef srcIs32ARGB
#undef dstIs32ARGB
 
#if NON_MAC_PIXEL_FORMATS
// 32BGRA - k32BGRAPixelFormat /* 32 bit bgra    (Matrox)*/
#define EffectFilter32 EffectFilter32BGRA
#define srcIs32BGRA 1
#define dstIs32BGRA 1
#include "EffectFilter32.c"
#undef EffectFilter32
#undef srcIs32BGRA
#undef dstIs32BGRA
// 32RGBA - k32RGBAPixelFormat /* 32 bit rgba    */
#define EffectFilter32 EffectFilter32RGBA
#define srcIs32RGBA 1
#define dstIs32RGBA 1
#include "EffectFilter32.c"
#undef EffectFilter32
#undef srcIs32RGBA
#undef dstIs32RGBA
// 32ABGR - k32ABGRPixelFormat /* 32 bit abgr    */
#define EffectFilter32 EffectFilter32ABGR
#define srcIs32ABGR 1
#define dstIs32ABGR 1
#include "EffectFilter32.c"
#undef EffectFilter32
#undef srcIs32ABGR
#undef dstIs32ABGR
#endif
 
// --------------------------------------------------------------------------------------
// INTERNAL ROUTINES
//      For clarity and reuse sake, the drawing code is being kept separate from the
//      component interface code.
// --------------------------------------------------------------------------------------
 
// RequestImageFormat
//      If the data is already in the requested height and depth, returns.
//  Otherwise, calls decompression to get it into the format we can handle
// --------------------------------------------------------------------------------------
static OSErr RequestImageFormat(EffectGlobals  *glob,       // input: globals for rendering
                                EffectSourcePtr source,     // input: source to potentially convert
                                short           width,      // input: desired width
                                short           height,     // input: desired height
                                OSType          pixelFormat)// input: desired pixel format (depth & format)
{
    OSErr                   err = noErr;
    CDSequenceDataSourcePtr sourceData = source->source.image;
    ImageDescriptionHandle  curDesc = (ImageDescriptionHandle)sourceData->dataDescription;
    ImageDescriptionHandle  newDesc = nil;
    ImageDescriptionPtr     dp;
 
    dp = *curDesc;
    if ((source->effectType == kEffectRawSource) && (((dp->cType == kRawCodecType) && (dp->depth == (short)pixelFormat)) || 
        (dp->cType == pixelFormat)) && (dp->width == width) && (dp->height == height))
    {
        /* already got what we need */
        return noErr;
    }
 
    // otherwise, call the ICM to convert to desired data format
    newDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
    err = MemError();
 
    if (err == noErr) {
        short pixelSize = QTGetPixelSize(pixelFormat);
        dp = *newDesc;
        
#if !NON_MAC_PIXEL_FORMATS
 
        if ((pixelFormat >> 24) != 0) {
 
            // non-Mac format
            dp->cType = pixelFormat;
            dp->depth = pixelSize;
 
        } else {
 
            dp->cType = kRawCodecType;
            dp->depth = pixelFormat;
        }
#else
        dp->cType = pixelFormat;
        dp->depth = pixelSize;
#endif
        dp->width  = width;
        dp->height = height;
        dp->clutID = -1;
        
        /* the source is a stacked effect - or one in a format we can't handle. */
        /* pass it off to the Generic Effect to convert */
        /* it to a normal source */
        err = ImageCodecEffectConvertEffectSourceToFormat(glob->target, source, newDesc);
 
        if (newDesc) {
            DisposeHandle((Handle) newDesc);
        }
    }
 
    return err;
}
 
// BlitterPreflight
// --------------------------------------------------------------------------------------
static long BlitterPreflight(BlitGlobals *glob,     // input: globals for rendering
                             short       width,     // input: width of data
                             short       height, // input: height of data
                             long        *depth)    // input/output: depth of data
{
    // *** CHANGE ***
    // If your effect handles different bit depths, change this code to return
    // what bit depths you want.
    
    // our blitter can handle 16 and 32 bit deep -- otherwise, we request a change to 16 bit
    switch (*depth) {
        case 16:
        case 32:
            break;
        
        default:
            *depth = 16;
            break;
    }
    // *** END CHANGE ***
 
    // save away the actual depth we are running at
    glob->width     = width;
    glob->height    = height;   
    glob->destDepth = *depth;
    
    return noErr;
}
 
// BlitterSetSource
// --------------------------------------------------------------------------------------
static long BlitterSetSource(EffectGlobals   *glob,         // input: our globals
                             long            sourceNumber,  // input: source index to set
                             EffectSourcePtr source)        // input: source value
{
    OSErr   err = noErr;
 
    if (sourceNumber >= kMaxSources) {
        // too many sources for us to handle
        err = -1;
    } else { 
        // a source we can handle, save it away
        err = RequestImageFormat(glob, source, glob->blitter.width, glob->blitter.height, glob->blitter.dstPixelFormat);
        if (err == noErr) {
            glob->blitter.sources[sourceNumber].src = source->source.image;
        } else {
            glob->blitter.sources[sourceNumber].src = nil;
        }
    }
    
    return err;
}
 
// BlitterSetDest
// --------------------------------------------------------------------------------------
static long BlitterSetDest(BlitGlobals  *glob,      // input: our globals
                           PixMap       *dstPixMap, // input: pixels we will draw into
                           Rect         *dstRect)   // input: area of pixels we will draw into
{
    OSErr   result = noErr;
    long    offsetH,offsetV;
    char    *baseAddr;
    OSType  dstPixelFormat;
 
    dstPixelFormat = GETPIXMAPPIXELFORMAT(dstPixMap);
 
    glob->dstPixelFormat = dstPixelFormat;
 
    // *** CHANGE ***
    // If your effect handles different bit depths, change this code adjust
    // the destination baseaddress to be at the beginning of the desired rect.
    offsetH = (dstRect->left - dstPixMap->bounds.left);
    if (dstPixMap->pixelSize == 16) {
        offsetH <<= 1;                      /* 1 pixel = 2 bytes */
    } else {
        if (dstPixMap->pixelSize == 32) {
            offsetH <<= 2;                  /* 1 pixel = 4 bytes */
        } else {
            result = -1;                    /* a data format we can't handle */
        }
    }
    // *** END CHANGE ***
 
    offsetV = (dstRect->top - dstPixMap->bounds.top) * QTGetPixMapPtrRowBytes(dstPixMap);
    baseAddr = dstPixMap->baseAddr + offsetH + offsetV;
 
    glob->dstBaseAddr = baseAddr;
    glob->dstRowBytes = QTGetPixMapPtrRowBytes(dstPixMap);
 
    return result;  
}
 
// BlitterRenderFrame
// --------------------------------------------------------------------------------------
static long BlitterRenderFrame(BlitGlobals  *glob) // input: our globals
{
    //SInt8 mmuMode;
 
    // render with proper memory mode
    //mmuMode = true32b;
    //SwapMMUMode(&mmuMode);
    
    // convert data into base/size
    {
        short   i;
        
        for (i = 0; i < kMaxSources; ++i) {
            if (glob->sources[i].src) {
                glob->sources[i].srcBaseAddr = glob->sources[i].src->dataPtr;
                glob->sources[i].srcRowBytes = glob->sources[i].src->dataSize / glob->height;
            }
        }
    }
        
    // *** CHANGE ***
    // If your effect handles different bit depths, write other bit depth
    // routines and call them from here.
    
    // do the actual render
    switch (glob->dstPixelFormat) {
        case k32ARGBPixelFormat:
            EffectFilter32ARGB(glob);
            break;
#if NON_MAC_PIXEL_FORMATS
        case k32ABGRPixelFormat:
            EffectFilter32ABGR(glob);
            break;
        case k32BGRAPixelFormat:     // we know how to do these pixel formats
            EffectFilter32BGRA(glob);
            break;
        case k32RGBAPixelFormat:
            EffectFilter32RGBA(glob);
            break;
#endif
        case k16BE555PixelFormat:
            EffectFilter16BE555(glob);
            break;
#if NON_MAC_PIXEL_FORMATS
        case k16LE565PixelFormat:
            EffectFilter16LE565(glob);
            break;
        case k16LE555PixelFormat:
            EffectFilter16LE555(glob);
            break;
#endif
    }
    // *** END CHANGE ***
 
    //SwapMMUMode(&mmuMode);
    
    return noErr;
}
 
#pragma mark-
// --------------------------------------------------------------------------------------
// COMPONENT ENTRY POINTS - Standard Component Calls
// --------------------------------------------------------------------------------------
 
// The number of supported pixel formats
#define kNumPixelFormatsSupported 0x20
 
/* -- This Effect Component uses the Generic Effect Component --
    The Generic Effect Component is an Apple-supplied component
    that makes it easier for developers to create new Effects.
    This component implements many of the "housekeeping" functions
    that all components must perform. In most cases, these default
    implementations are appropriate for your effect, and you simply
    delegate these functions to the generic effect component.
*/
 
// Component Open Request - Required
// This is called once per instance of our component.  Allocate our storage at
// this point.  If we have any shared storage, we would check here to make sure
// it exists, else create it.
// input/output: our globals
// input: reference to ourself
pascal ComponentResult EffectsFrameOpen(EffectGlobals       *glob,
                                        ComponentInstance   self)
{
    ComponentResult result;
    
    result = noErr;
        
    // first, allocate our local storage
    if ((glob = (EffectGlobals*) NewPtrClear(sizeof(EffectGlobals))) == nil)
    {
        result = MemError();
        goto bail;
    }
    
    SetComponentInstanceStorage(self, (Handle) glob);
    
    // we are ourselves, and the current top of chain is us
    glob->self = self;
    glob->target = self;
    glob->wantedDestinationPixelTypeH = (OSType **)NewHandleClear(sizeof(OSType) * (kNumPixelFormatsSupported + 1));
    
    // open the generic effect, this will handle effects we can't handle ourselves
    result = OpenADefaultComponent(decompressorComponentType, kEffectGenericType, &glob->delegate);
    if (result) goto bail;
        
    // set up the target for the components below us
    ComponentSetTarget(glob->delegate, self);
 
bail:
    return result;
}
 
// Component Close Request - Required
// Called each time an instance of our component is going away.  Toss anything we allocated.
// input: our globals
// input: reference to ourself
pascal ComponentResult EffectsFrameClose(EffectGlobals      *glob,
                                         ComponentInstance  self)
{
#pragma unused (self)
 
    if (glob)
    {
        /*  *** CHANGE *** DISPOSE OF YOUR TWEENERS */
        DisposeTweenRecord(&glob->percentage);
 
        /* *** END CHANGE *** */
 
        DisposeTweenGlobals(&glob->tweenGlobals);
 
        CloseComponent(glob->delegate);
 
        DisposeHandle((Handle) glob->wantedDestinationPixelTypeH);
 
        DisposePtr((Ptr) glob);
    }
    
    return noErr;
}
 
// Component Target Request
//      Allows another component to "target" you i.e., you call another component whenever
// you would call yourself (as a result of your component being used by another component).
// input: our globals
// input: reference to new top of chain
pascal ComponentResult EffectsFrameTarget(EffectGlobals     *glob,
                                          ComponentInstance target)
{
    // remember who is top of chain
    glob->target = target;
    
    // and tell folks below us, too.
    ComponentSetTarget(glob->delegate, target);
    
    return noErr;
}
 
// Component Version Request - Required
//      Called to obtain the version of our component.
//  input: our globals
pascal ComponentResult EffectsFrameVersion(EffectGlobals    *glob)
{
#pragma unused (glob)
    
    return kDimmerEffectVersion;
}
 
#pragma mark-
// --------------------------------------------------------------------------------------
// COMPONENT ENTRY POINTS - Image Codec Calls
// --------------------------------------------------------------------------------------
 
// ImageCodecGetCodecInfo
//      Your component receives the ImageCodecGetCodecInfo request whenever an application calls the
//  Image Compression Manager's GetCodecInfo function. Your component should return a formatted compressor
//  information structure defining its capabilities.
//  The info is stored as a resource in our component.
//
//      input: our globals
//      output: our codec info
// ----------------------------------------------------------------------------------------
pascal ComponentResult EffectsFrameGetCodecInfo(EffectGlobals   *glob,
                                                CodecInfo       *info)
{
    OSErr err = noErr;
 
    if (info == nil)
    {
        err = paramErr;
    }
    else
    {
        CodecInfo **tempCodecInfo;
 
        err = GetComponentResource((Component) glob->self,
                                   codecInfoResourceType,
                                   kEffect_cdci_ResID,
                                   (Handle *)&tempCodecInfo);
        if (err == noErr)
        {
            *info = **tempCodecInfo;
            DisposeHandle((Handle)tempCodecInfo);
        }
    }
 
    return err;
}
 
// ImageCodecGetParameterListHandle
//      Returns a parameter description atom container, as described in the QuickTime Effect Documentaion
//  section "Supplying Parameter Description Information".
//  http://developer.apple.com/techpubs/quicktime/qtdevdocs/REF/refEffects.28.htm
//  This function can use the GetComponentResource function to retrieve an 'atms' resource that stores this
//  information if you have provided one in your component.
//
//      input: our globals
//      output: the parameter description for this effect
// ----------------------------------------------------------------------------------------
pascal ComponentResult EffectsFrameGetParameterListHandle(EffectGlobals *glob,
                                                          Handle        *theHandle)
{
    OSErr err = noErr;
    
    err = GetComponentResource((Component)glob->self,
                               FOUR_CHAR_CODE('atms'),
                               kEffect_atms_ResID,
                               theHandle);
    return err;
}
 
// ImageCodecValidateParameters - Optional
//      If your effect implements this optional function, the Component Manager calls it whenever the user
//  changes a parameter value in the standard parameter dialog box, or attempts to dismiss the dialog by clicking OK.
//  The purpose of this function is to allow your effect to validate its parameters. The current parameter values
//  are passed to the effect in parameters . If all of these values are valid, this function should return noErr.
//  Otherwise, you should return a paramErr and put an explanatory message in the errorString parameter.
//
//      input: our globals
//      input: the current parameter values for this effect
//      input: flags that indicate whether a parameter value has changed or the user is
//             dismissing the standard parameter dialog box.
//
//              kParameterValidationNoFlags - This value indicates that a standard validation should take place.
//                                            This function is being called because the user has changed the
//                                            value of a parameter in the standard parameters dialog box. 
//              kParameterValidationFinalValidation - This value indicates that this validation is the final validation
//                                                    before the standard parameters dialog box is dismissed. This is useful
//                                                    if you want to perform a single validation of the parameters just after
//                                                    the user clicks the OK button to dismiss the dialog box. 
//
//      output: a StringPtr that is contains an error string explaining to the user why
//              the validation has failed.
// ----------------------------------------------------------------------------------------  
pascal ComponentResult EffectsFrameValidateParameters(EffectGlobals *glob,
                                                      QTAtomContainer parameters,
                                                      QTParameterValidationOptions validationFlags,
                                                      StringPtr errorString)
{
#pragma unused (glob, parameters, validationFlags, errorString)
 
    return noErr;
}
 
#pragma mark-
// --------------------------------------------------------------------------------------
// COMPONENT ENTRY POINTS - Effect Codec Calls
// --------------------------------------------------------------------------------------
 
// ImageCodecEffectSetup
//      Called once before a sequence of frames are rendered. This gives your effect the chance
// to set up variables that will alter their value during the execution of a sequence of frames.
// Your component should examine the capabilities field of the decompressParams data structure to
// ensure that it can meet the requirements for executing this sequence. In particular, it should
// check the bit depth and pixel format requirements of the sequence. If the sequence requires a
// bit depth and pixel format combination that your component does not support, this function should
// return the nearest supported combination in the decompressParams->capabilities field.
// In this case, QuickTime will redirect all source and destination bitmaps through offscreen graphics
// worlds that have the bit depth and pixel format characteristics that you specify.
// 
//      input: our globals
//      input: information about the thing being decompressed
//
//  Return in p->capabilities anything in particular your effect requires, such as 
//  limitations on bitdepth.
// --------------------------------------------------------------------------------------
pascal long EffectsFrameEffectSetup(EffectGlobals           *glob,
                                    CodecDecompressParams   *p)
{
    CodecCapabilities   *capabilities = p->capabilities;
    OSErr               err;
    OSType              *formats = *glob->wantedDestinationPixelTypeH;
    long                wantedPixelSize = capabilities->wantedPixelSize;
    OSType              dstPixelFormat;
    
    dstPixelFormat = GETPIXMAPPIXELFORMAT(&p->dstPixMap);
 
    switch (dstPixelFormat)
    {
        case k32ARGBPixelFormat:
        case k16BE555PixelFormat:
#if NON_MAC_PIXEL_FORMATS
        case k32BGRAPixelFormat:     // we know how to do these pixel formats
        case k32ABGRPixelFormat:
        case k32RGBAPixelFormat:
        case k16LE565PixelFormat:
        case k16LE555PixelFormat:
#endif
            *formats++ = dstPixelFormat;
            break;
        default:                    // we prefer 16 over 32
#if NON_MAC_PIXEL_FORMATS
            *formats++ = k16LE555PixelFormat;
            *formats++ = k16LE565PixelFormat;
            *formats++ = k32BGRAPixelFormat;
            *formats++ = k32RGBAPixelFormat;
            *formats++ = k32ABGRPixelFormat;
#endif
            *formats++ = k16BE555PixelFormat;
            *formats++ = k32ARGBPixelFormat;
            break;
    }
    
    // NOTE: 0 marks the end of the format list
    *formats++ = 0;
 
    /* set up our blitter */
    err = BlitterPreflight(&glob->blitter,
                           (*p->imageDescription)->width,
                           (*p->imageDescription)->height,
                           &wantedPixelSize);
    
    capabilities->wantedPixelSize = 0;
 
    p->wantedDestinationPixelTypes = glob->wantedDestinationPixelTypeH;
 
    return err;
}
 
// ImageCodecEffectBegin
//      This function is called immediately before your EffectRenderFrame function.
//  The EffectBegin function should ensure that the information it holds about the current source
//  and destination buffers and the parameter values for the effect are valid. If any of these have
//  changed since the last call to EffectBegin, the new values should be read from the appropriate
//  data structures. This function is guaranteed to be called synchronously. In particular, this means
//  you can allocate and move memory, and can call functions that allocate or move memory.
//
//      input: our globals
//      input: info about frame being drawn
//      input: info about this effect frame
// --------------------------------------------------------------------------------------
pascal long EffectsFrameEffectBegin(EffectGlobals           *glob,
                                    CodecDecompressParams   *p,
                                    EffectsFrameParamsPtr   effect)
{
    OSErr           err = noErr;
    EffectSourcePtr source;
    long            numSources = 0;
    wide            percentage;
 
    #if kMaxAsyncFrames > 0
        /* we can go async if we don't already have an effect scheduled */
        if (glob->asyncCount < kMaxAsyncFrames)
        {
            glob->asyncCount++;
            effect->doAsync = true;
        }
    #endif
 
    // dest changed? 
    if (p->conditionFlags & (codecConditionNewClut+codecConditionFirstFrame+codecConditionNewDepth+
                             codecConditionNewDestination+codecConditionNewTransform))
    {
        // re-scan the sources
        glob->majorSourceChangeSeed = 0;
 
        // re-read the parameters
        glob->frameNumber = 0;
 
        err = BlitterSetDest(&glob->blitter, &p->dstPixMap, &p->dstRect);
        if (err != noErr) goto bail;
    }
 
    // new sources?  make note of them!
    if (p->majorSourceChangeSeed != glob->majorSourceChangeSeed)
    {
        // grab start of input chain for this effect
        source = effect->source;
 
        /* we can play with up to kMaxSources sources, so go get them */
        while (source != nil && numSources < kMaxSources)
        {       
            /* now give that source to our blitter */
            err = BlitterSetSource(glob, numSources, source);
            if (err != noErr) goto bail;
                
            source = source->next;
            ++numSources;
        }
        
        glob->majorSourceChangeSeed = p->majorSourceChangeSeed;
    }
    
    /* if this is a new frame, or the same frame with a new length, get rid of our old parameters & tweeners */ 
    if ((effect->frameTime.frameNumber != glob->frameNumber) || (effect->frameTime.virtualDuration != glob->virtualDuration) )
    {
        /*  *** CHANGE *** DISPOSE OF YOUR TWEENERS */
        DisposeTweenRecord(&glob->percentage);
        
        /* *** END CHANGE *** */
 
        DisposeTweenGlobals(&glob->tweenGlobals);
 
        glob->initialized = false;
        glob->frameNumber = effect->frameTime.frameNumber;
        glob->virtualDuration = effect->frameTime.virtualDuration;
    }
 
    // Read in effect parameters
    if (!glob->initialized)
    {
        Ptr                     data = p->data;
        OSErr                   err;
        long                    index = 1;
        
        err = InitializeTweenGlobals(&glob->tweenGlobals, p);
        if (err!=noErr)
            goto bail;
            
        /*  *** CHANGE *** TIME TO READ IN PARAMETERS TO YOUR EFFECT:
        
            You can read any number of atoms you wish from the container.  For example, you might have a 
            'star' (start) and 'end ' (end) value.  They might be expressed as percentages, numbers, or other values.
            Or, you might have multiple atoms of type 'para' (param) which would be read in by calling
            QTFindChildByIndex with various index values.  If you want to know how many parameters of a given
            type there are, call QTCountChildrenOfType.  These parameters are specific to your particular effect,
            and will need to be placed there by whoever is authoring the title.
            
            If you require parameters, and they aren't there, return an error.  If you can default the values
            if they are missing, do so and continue.  In general, I'd recommend having a default case unless
            you really are unable to implement it for a technical reason.
            
            These parameters are those that apply to the effect itself.  Later, we'll translate some of these parameters
            into how they relate to *where* in the effect we are.  For example, an effect that runs from a starting
            percentage of 10 to an ending percentage of 90 will have 10 and 90 as parameters here.
        */
        
        /* make our tweener, return if we already have it */
        err = CreateTweenRecord(&glob->tweenGlobals, &glob->percentage, 
                                kParameterUsagePercent, 1, sizeof(Fixed),
                                kTweenTypeFixed, (void*)0, (void*)fixed1,  
                                effect->frameTime.virtualDuration);
        if (err != noErr) goto bail;
 
        /* *** END CHANGE *** */
 
        glob->initialized = true;
    }
    
    /* determine the amount we are along the tween via the current time - start time */
    percentage = effect->frameTime.value;
    CompSub(&effect->frameTime.virtualStartTime, &percentage);
    
    /*  Tween our parameters and get the current value for this frame, prepare to render
        it when the RenderFrame happens. */
    if (err == noErr)
    {
        Fixed   thePercentage;
        
        /* ***** CHANGE TO TWEEN YOUR EFFECTS PARAMETERS */
        
        if (glob->percentage.tween)
            QTDoTween(glob->percentage.tween, percentage.lo, glob->percentage.tweenData, nil, nil, nil );
        
        thePercentage = **(Fixed**)(glob->percentage.tweenData);
        // If we are before the half-way point of this transition, we should
        // be fading the first source to black
        if (thePercentage < fixed1 / 2)
        {
            (glob->blitter).direction = 1;
            (glob->blitter).dimValue = Fix2Long(FixMul(Long2Fix(255*2), thePercentage));
        }
        // Otherwise, we are fading up onto the new source
        else
        {
            (glob->blitter).direction = 0;
            (glob->blitter).dimValue = Fix2Long(FixMul(Long2Fix(255*2), thePercentage)) - 255;
        }
 
        /* ***** END CHANGE */
    }
 
    if (glob->tweenGlobals.atLeastOneTweener)
    {
        // this effect runs constantly
        p->needUpdateOnTimeChange = true;
        p->needUpdateOnSourceChange = false;
    }
    else
    {
        // this effect only needs to run when the sources actually change
        p->needUpdateOnTimeChange = false;
        p->needUpdateOnSourceChange = true;
    }
    
        
// EXCEPTION HANDLING
bail:
    #if kMaxAsyncFrames > 0
        // if we didn't queue the frame, then remove it from used list
        if (err != noErr)
            glob->asyncCount--;
    #endif
    
    return err;
}
 
// ImageCodecEffectRenderFrame
//      Called to render a frame. Because this function can be called asynchronously, it is not safe
//  to perform operations that may move memory during this call. This function contains the implementation
//  of your effect.
//
//      input: our global
//      input: effect frame to be rendered
// --------------------------------------------------------------------------------------
pascal long EffectsFrameEffectRenderFrame(EffectGlobals         *glob,
                                          EffectsFrameParamsPtr effect)
{
#pragma unused (effect)
 
    /* render the frame */
    BlitterRenderFrame(&glob->blitter);
    
    #if kMaxAsyncFrames > 0
        glob->asyncCount--;
    #endif
 
    return noErr;
    
}
 
// ImageCodecEffectCancel
//      Cancel the rendering of a sequence of frames before the last frame has been rendered. If your
//  component supports asynchronous operation, this function can be called while a frame is being rendered.
//  If your component is running synchronously, it should simply return noErr - no further calls to your
//  EffectRenderFrame function will be made for this sequence. If your component is running asynchronously,
//  this function should dequeue all outstanding render frame requests, then return noErr.
//
//      input: our globals
//      input: effect frame to be canceled
// --------------------------------------------------------------------------------------
pascal long EffectsFrameEffectCancel(EffectGlobals          *glob,
                                    EffectsFrameParamsPtr   effect)
{
#pragma unused (effect)
#pragma unused (glob)
 
    #if kMaxAsyncFrames > 0
        glob->asyncCount--;
    #endif
 
    return noErr;   
}
 
// ImageCodecEffectGetSpeed
//      Returns the approximate number of frames per second that your effect is capable of transforming.
//  This function should return a Fixed value in pFPS , which represents the rendering speed in frames-per-second
//  of the effect. If your effect can render in real time, it should return a value of effectIsRealtime.
//  Otherwise, you should return an estimate of the number of frames your effect can render per second.
//  Because rendering speeds are hardware-dependent, effect authors can choose to measure actual rendering
//  speeds in this function. Alternatively, effect authors can choose to return a single value for all hardware
//  configurations, estimating the value for a reference hardware platform.
//  Apple recommends that the values returned are rounded down to the nearest common frames-per-second value,
//  such as 15, 24 or 30.
//
//      input: our globals
//      input: the current parameter values for this effect
//      output: a Fixed value that will contain the rendering speed of this effect on exit
// ----------------------------------------------------------------------------------------
pascal long EffectsFrameEffectGetSpeed(EffectGlobals * glob,
                                       QTAtomContainer parameters,
                                       Fixed *pFPS)
{
#pragma unused (glob, parameters)
 
    if (pFPS)
        *pFPS = Long2Fix(30);
        
    return noErr;
}