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.
GreyscaleEffect.c
/* |
File: GreyscaleEffect.c |
Description: Sample QuickTime effect component |
Author: era |
Copyright: © Copyright 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/18/02 era first file |
*/ |
/* |
Greyscale Filter Effect - a sample QuickTime video effect. |
This effect uses a single source as input, and renders that source in |
greyscale. It also allows for a brightness value which can range from |
-100...0...100. A value of 0 signifies no change in brightness. |
This is a very basic sample and a good place to start if you've never looked at |
QuickTime effect code before. |
This sample makes use of some code from the Dimmer2 Effect Framework. While Dimmer2 is |
a recommended companion to this sample, it is more complex, using multiple sources, |
multiple pixel formats, non-Macintosh support, tweens etc. |
For developers who have asked for a quick and dirty single source filter sample, this one's |
for you...enjoy. |
*/ |
// -------------------------------------------------------------------------------------- |
// 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" |
// -------------------------------------------------------------------------------------- |
// INTERNAL TYPEDEFS |
// -------------------------------------------------------------------------------------- |
// Structure used to store information about the 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 source; // inputs |
void *dstBaseAddr; // output base address |
long dstRowBytes; // output row bytes |
long height; // output height |
long width; // output width |
OSType dstPixelFormat; // output pixel format |
long brightValue; // brightness value, from -100 to 100 |
} BlitGlobals; |
// global data per instance. This holds data for the entire effect as it runs its course. |
typedef struct { |
ComponentInstance self; // us |
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; |
// parameter/source/dest seed tracking |
long initialized; |
long frameNumber; |
long virtualDuration; |
long majorSourceChangeSeed; |
} 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() GreyscaleFilter |
#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 - This is a "basic" sample so we only support one pixel format |
// k32ARGBPixelFormat 32 bit argb (Mac). See Dimmer2 Effect for a more |
// complete cross platform implementation. |
// -------------------------------------------------------------------------------------- |
// -------------------------------------------------------------------------------------- |
// INTERNAL ROUTINES |
// -------------------------------------------------------------------------------------- |
#define Get32(x) (*(long*)(x)) |
#define Set32(x,y) (*(long*)(x)) = ((long)(y)) |
// There are two versions of the EffectFilter32 function (the actual blitter code), they |
// do the exact same thing but the second one was happily optimized by geowar for fun. |
// Note, this code is intended as an example, yours will hopefully be cooler, faster, more bionic |
#define OPTIMIZED 1 |
#if !OPTIMIZED |
// Code that draws our actual effect. |
static void EffectFilter32(BlitGlobals *glob) |
{ |
long height = glob->height; |
long *src = glob->source.srcBaseAddr; |
long *dst = glob->dstBaseAddr; |
long srcBump = glob->source.srcRowBytes - (glob->width * 4); |
long dstBump = glob->dstRowBytes - (glob->width * 4); |
while (height--) |
{ |
long width = glob->width; |
while (width--) |
{ |
UInt32 thePixelValue; |
UInt8 theGrayValue; |
SInt16 adjust; |
thePixelValue = Get32(src); |
src++; |
// Grey scale filter, each pixel has a value ranging from 0 (black) to 255 (white) |
// The transformation is based on the equation: |
// |
// grey value = 0.299*r + 0.587*g + 0.114*b (see [Russ95], pp. 39) |
// |
// r, g, b indicate the red, green, blue color values of a pixel in the original image |
theGrayValue = (0.299 * ((thePixelValue >> 16) & 0xff)) + |
(0.587 * ((thePixelValue >> 8) & 0xff)) + |
(0.114 * ((thePixelValue >> 0) & 0xff)); |
// figrue out the brightness adjustment |
if (glob->brightValue < 0 ) |
adjust = glob->brightValue * theGrayValue / 100; |
else |
adjust = glob->brightValue * (255 - theGrayValue) / 100; |
theGrayValue += adjust; |
thePixelValue = (0xff000000) | (theGrayValue << 16) | (theGrayValue << 8) | (theGrayValue << 0); // alpha (opaque) |
Set32(dst,thePixelValue); |
dst++; |
} |
src = (void *)(((Ptr)src) + srcBump); |
dst = (void *)(((Ptr)dst) + dstBump); |
} |
} |
#else |
// Optimized code that draws our actual effect. |
static void EffectFilter32(BlitGlobals *glob) |
{ |
long height = glob->height; |
long *src = glob->source.srcBaseAddr; |
long *dst = glob->dstBaseAddr; |
long srcBump = glob->source.srcRowBytes - (glob->width * 4); |
long dstBump = glob->dstRowBytes - (glob->width * 4); |
// pre-compute the adjustment factors and convert the number to a power of two so |
// the shift operator can be used instead of a divide when applying the adustment |
SInt32 thePixelValue; |
UInt8 theGrayValue; |
SInt32 posAdj = 0, |
negAdj = 0; |
long theBrightValue = 128 * glob->brightValue / 100; |
// do we have a pos or neg adjustment to make? |
(theBrightValue < 0) ? (negAdj = theBrightValue) : (posAdj = theBrightValue); |
while (height--) |
{ |
long width = glob->width; |
while (width--) |
{ |
thePixelValue = Get32(src); |
src++; |
// Grey scale filter, each pixel has a value ranging from 0 (black) to 255 (white) |
// The transformation is based on the equation: |
// |
// grey value = 0.299*r + 0.587*g + 0.114*b (see [Russ95], pp. 39) |
// |
// r, g, b indicate the red, green, blue color values of a pixel in the original image |
// Compare this code to the original implemtation above |
// 1. conversion between ints and floats is slow - avoid by using int fractions |
// (299*r / 1000) + (587*g / 1000)+ (114*b/1000) |
// 2. combine all the divides |
// (299*r + 587*g + 114*b) / 1000 |
// 3. convert weighting factors to power of two so a shift can be used instead of a divide |
// 299 / 1000 == 306 / 1024 == 306 >> 10 |
// 587 / 1000 == 601 / 1024 == 601 >> 10 |
// 114 / 1000 == 114 / 1024 == 114 >> 10 |
// Zippy! |
theGrayValue = ((306 * ((thePixelValue >> 16) & 0xff)) + |
(601 * ((thePixelValue >> 8) & 0xff)) + |
(117 * ((thePixelValue >> 0) & 0xff))) >> 10; |
// do the brightness adjustment |
// the branch statement is eliminated and replaced with the positive and |
// negative adjustment variables, one of which should be zero |
// pre-converted power of two values so a shift can be used instead of a divide |
theGrayValue += ((theGrayValue * negAdj) + ((255 - theGrayValue) * posAdj)) >> 7; |
// alpha (opaque) |
thePixelValue = (0xff000000) | (theGrayValue << 16) | (theGrayValue << 8) | (theGrayValue << 0); |
Set32(dst,thePixelValue); |
dst++; |
} |
src = (void *)(((Ptr) src) + srcBump); |
dst = (void *)(((Ptr) dst) + dstBump); |
} |
} |
#endif // OPTIMIZED |
// BlitterRenderFrame |
// -------------------------------------------------------------------------------------- |
static long BlitterRenderFrame(BlitGlobals *blitGlob) // input: our globals |
{ |
// convert data into base/size |
if (blitGlob->source.src) { |
blitGlob->source.srcBaseAddr = blitGlob->source.src->dataPtr; |
blitGlob->source.srcRowBytes = blitGlob->source.src->dataSize / blitGlob->height; |
} |
// do the actual render |
switch (blitGlob->dstPixelFormat) { |
case k32ARGBPixelFormat: |
EffectFilter32(blitGlob); |
break; |
} |
return noErr; |
} |
// RequestImageFormat |
// If the data is already in the requested height and depth, return. |
// 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) |
{ |
CDSequenceDataSourcePtr sourceData = source->source.image; |
ImageDescriptionHandle curDesc = (ImageDescriptionHandle)sourceData->dataDescription; |
ImageDescriptionHandle newDesc = NULL; |
ImageDescriptionPtr dp; |
OSErr err = noErr; |
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 (noErr == err) { |
short pixelSize = QTGetPixelSize(pixelFormat); |
dp = *newDesc; |
dp->cType = pixelFormat; |
dp->depth = pixelSize; |
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; |
} |
#pragma mark- |
// -------------------------------------------------------------------------------------- |
// COMPONENT ENTRY POINTS - Standard Component Calls |
// -------------------------------------------------------------------------------------- |
// The number of supported pixel formats |
#define kNumPixelFormatsSupported 1 |
/* -- 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 GreyscaleFilterOpen(EffectGlobals *glob, |
ComponentInstance self) |
{ |
ComponentResult result; |
result = noErr; |
// first, allocate our local storage |
if ((glob = (EffectGlobals *)NewPtrClear(sizeof(EffectGlobals))) == NULL) |
{ |
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 GreyscaleFilterClose(EffectGlobals *glob, |
ComponentInstance self) |
{ |
#pragma unused (self) |
if (glob) |
{ |
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 GreyscaleFilterTarget(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 GreyscaleFilterVersion(EffectGlobals *glob) |
{ |
#pragma unused (glob) |
return kEffectVersion; |
} |
#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 GreyscaleFilterGetCodecInfo(EffectGlobals *glob, |
CodecInfo *info) |
{ |
OSErr err = noErr; |
if (info == NULL) |
{ |
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 GreyscaleFilterGetParameterListHandle(EffectGlobals *glob, |
Handle *theHandle) |
{ |
OSErr err = noErr; |
err = GetComponentResource((Component)glob->self, |
kEffectAtomsResType, |
kEffect_atms_ResID, |
theHandle); |
return err; |
} |
#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 GreyscaleFilterEffectSetup(EffectGlobals *glob, |
CodecDecompressParams *p) |
{ |
CodecCapabilities *capabilities = p->capabilities; |
OSType *formats = *glob->wantedDestinationPixelTypeH; |
long wantedPixelSize = capabilities->wantedPixelSize; |
OSType dstPixelFormat; |
OSErr err = noErr; |
dstPixelFormat = GETPIXMAPPIXELFORMAT(&p->dstPixMap); |
switch (dstPixelFormat) { |
case k32ARGBPixelFormat: // we know how to do these pixel formats |
*formats++ = dstPixelFormat; |
break; |
default: // we really need this! |
*formats++ = k32ARGBPixelFormat; |
break; |
} |
// end of the format list |
*formats++ = 0; |
/* set up our blitter */ |
glob->blitter.width = (*p->imageDescription)->width; |
glob->blitter.height = (*p->imageDescription)->height; |
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 GreyscaleFilterEffectBegin(EffectGlobals *glob, |
CodecDecompressParams *p, |
EffectsFrameParamsPtr effect) |
{ |
EffectSourcePtr source; |
OSErr err = noErr; |
long offsetH, offsetV; |
// we don't async |
effect->doAsync = false; |
// Destination: |
// has the destination changed? |
if (p->conditionFlags & (codecConditionNewClut+codecConditionFirstFrame+ |
codecConditionNewDepth+codecConditionNewDestination+ |
codecConditionNewTransform)) |
{ |
// this will make us re-scan the sources |
glob->majorSourceChangeSeed = 0; |
// this will make us re-read the parameter values |
glob->frameNumber = 0; |
// set up the blitter globals for the destination |
glob->blitter.dstPixelFormat = GETPIXMAPPIXELFORMAT(&p->dstPixMap); |
// adjust the destination baseaddress to be at the beginning of the desired rect |
offsetH = (p->dstRect.left - p->dstPixMap.bounds.left); |
if (p->dstPixMap.pixelSize == 32) { |
offsetH <<= 2; /* 1 pixel = 4 bytes */ |
} else { |
err = codecConditionErr; /* a data format we can't handle */ |
goto bail; |
} |
offsetV = (p->dstRect.top - p->dstPixMap.bounds.top) * QTGetPixMapPtrRowBytes(&p->dstPixMap); |
glob->blitter.dstBaseAddr = p->dstPixMap.baseAddr + offsetH + offsetV; |
glob->blitter.dstRowBytes = QTGetPixMapPtrRowBytes(&p->dstPixMap); |
} |
// Source: |
// if it's a new source make note of it! |
if (p->majorSourceChangeSeed != glob->majorSourceChangeSeed) |
{ |
// grab start of input chain for this effect |
source = effect->source; |
// set the blitters source |
// if it's a source we can handle, save it away, if not bail |
err = RequestImageFormat(glob, source, glob->blitter.width, glob->blitter.height, glob->blitter.dstPixelFormat); |
if (noErr == err) { |
glob->blitter.source.src = source->source.image; |
} else { |
glob->blitter.source.src = NULL; |
goto bail; |
} |
glob->majorSourceChangeSeed = p->majorSourceChangeSeed; |
} |
// If this is a new frame, or the same frame with a new length, get rid of our old parameters |
if ((effect->frameTime.frameNumber != glob->frameNumber) || (effect->frameTime.virtualDuration != glob->virtualDuration)) |
{ |
glob->initialized = false; |
glob->frameNumber = effect->frameTime.frameNumber; |
glob->virtualDuration = effect->frameTime.virtualDuration; |
} |
// Read in effect parameters |
if (!glob->initialized) |
{ |
Ptr data = p->data; |
QTAtom atom; |
QTAtomID atomID = 1; |
long actSize; |
// Find the 'bryt' atom |
atom = QTFindChildByID((QTAtomContainer)&data, |
kParentAtomIsContainer, |
kEffectBrightnessCtrlAtom, // The name of the parameter |
atomID, // The ID of the parameter |
NULL); |
// Copy the parameter value from the atom |
if (QTCopyAtomDataToPtr((QTAtomContainer)&data, |
atom, |
false, |
sizeof(long), |
&((glob->blitter).brightValue), |
&actSize) != noErr) |
{ |
// If the copy failed, use a default value for this parameter |
(glob->blitter).brightValue = 0; |
} |
glob->initialized = true; |
} |
// this effect only needs to run when the sources actually change |
p->needUpdateOnTimeChange = false; |
p->needUpdateOnSourceChange = true; |
bail: |
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 GreyscaleFilterEffectRenderFrame(EffectGlobals *glob, |
EffectsFrameParamsPtr effect) |
{ |
#pragma unused (effect) |
// render the frame |
BlitterRenderFrame(&glob->blitter); |
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 GreyscaleFilterEffectGetSpeed(EffectGlobals * glob, |
QTAtomContainer parameters, |
Fixed *pFPS) |
{ |
#pragma unused (glob, parameters) |
if (pFPS) *pFPS = Long2Fix(30); |
return noErr; |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-02-25