softVdig.c

/*
    File:       softVdig.c
 
    Contains:   Software video digitzer routines
 
    Written by: Peter Hoddie (mostly) and Casey King and Gary Woodcock
    
                Refer to develop Issue 14, "Video Digitizing Under QuickTime",
                for details on this code.
                
                This code requires QuickTime 1.5.
 
    Copyright:  © 1993 by Apple Computer, Inc.
 
*/
 
//-----------------------------------------------------------------------
// includes
 
#include "softVdig.h"
#include <FixMath.h>
#include <MacErrors.h>
#include <Packages.h>
#include <Fonts.h>
#include <MacMemory.h>
#include <ToolUtils.h>
#include <NumberFormatting.h>
 
//-----------------------------------------------------------------------
 
#if TARGET_OS_WIN32
    #pragma warning (disable: 4761)     // ignore integral size mismatch in argument
#endif
 
#if defined(DEBUG_IT) || !TARGET_OS_MAC
// Use this declaration when we're running linked (for debugging)
pascal ComponentResult softVdig (ComponentParameters *params, Handle storage)
 
#else
 
// Use this declaration when we're a standalone component
pascal ComponentResult main(ComponentParameters *params, Handle storage)
 
#endif DEBUG_IT
 
{
    // This routine is the main dispatcher for the softVdig
    
    ComponentRoutineUPP vdigProc = 0;
    ComponentResult err = 0;
 
    if (params->what < 0) {
        switch(params->what) {
            case kComponentOpenSelect:              vdigProc = (ComponentRoutineUPP)vdigOpen; break;
            case kComponentCloseSelect:             vdigProc = (ComponentRoutineUPP)vdigClose; break;
            case kComponentCanDoSelect:             vdigProc = (ComponentRoutineUPP)vdigCanDo; break;
            case kComponentVersionSelect:           vdigProc = (ComponentRoutineUPP)vdigVersion; break;
        }
    }
    else {
        switch (params->what) {
            case kVDGetMaxSrcRectSelect:            vdigProc = (ComponentRoutineUPP)vdigGetMaxSrcRect; break;
            case kVDGetActiveSrcRectSelect:         vdigProc = (ComponentRoutineUPP)vdigGetActiveSrcRect; break;
            case kVDSetDigitizerRectSelect:         vdigProc = (ComponentRoutineUPP)vdigSetDigitizerRect; break;
            case kVDGetDigitizerRectSelect:         vdigProc = (ComponentRoutineUPP)vdigGetDigitizerRect; break;
            case kVDUseThisCLUTSelect:              vdigProc = (ComponentRoutineUPP)vdigUseThisCLUT; break;
            case kVDGrabOneFrameSelect:             vdigProc = (ComponentRoutineUPP)vdigGrabOneFrame; break;
            case kVDGetMaxAuxBufferSelect:          vdigProc = (ComponentRoutineUPP)vdigGetMaxAuxBuffer; break;
            case kVDGetDigitizerInfoSelect:         vdigProc = (ComponentRoutineUPP)vdigGetDigitizerInfo; break;
            case kVDGetCurrentFlagsSelect :         vdigProc = (ComponentRoutineUPP)vdigGetCurrentFlags; break;
            case kVDSetPlayThruDestinationSelect:   vdigProc = (ComponentRoutineUPP)vdigSetPlayThruDestination; break;
            case kVDSetBrightnessSelect :           vdigProc = (ComponentRoutineUPP)vdigSetBrightness; break;
            case kVDGetBrightnessSelect :           vdigProc = (ComponentRoutineUPP)vdigGetBrightness; break;   
            case kVDSetContrastSelect :             vdigProc = (ComponentRoutineUPP)vdigSetContrast; break;         
            case kVDSetHueSelect :                  vdigProc = (ComponentRoutineUPP)vdigSetHue; break;
            case kVDSetSaturationSelect :           vdigProc = (ComponentRoutineUPP)vdigSetSaturation; break;
            case kVDSetSharpnessSelect :            vdigProc = (ComponentRoutineUPP)vdigSetSharpness; break;
            case kVDGetContrastSelect :             vdigProc = (ComponentRoutineUPP)vdigGetContrast; break;
            case kVDGetHueSelect :                  vdigProc = (ComponentRoutineUPP)vdigGetHue; break;
            case kVDGetSaturationSelect :           vdigProc = (ComponentRoutineUPP)vdigGetSaturation; break;
            case kVDGetSharpnessSelect :            vdigProc = (ComponentRoutineUPP)vdigGetSharpness; break;
            case kVDSetBlackLevelValueSelect :      vdigProc = (ComponentRoutineUPP)vdigSetBlackLevel; break;
            case kVDGetBlackLevelValueSelect :      vdigProc = (ComponentRoutineUPP)vdigGetBlackLevel; break;
            case kVDSetWhiteLevelValueSelect :      vdigProc = (ComponentRoutineUPP)vdigSetWhiteLevel; break;
            case kVDGetWhiteLevelValueSelect :      vdigProc = (ComponentRoutineUPP)vdigGetWhiteLevel; break;
            case kVDGetVideoDefaultsSelect :        vdigProc = (ComponentRoutineUPP)vdigGetVideoDefaults; break;    
            case kVDSetPlayThruOnOffSelect:         vdigProc = (ComponentRoutineUPP)vdigSetPlayThruOnOff; break;
            case kVDPreflightDestinationSelect:     vdigProc = (ComponentRoutineUPP)vdigPreflightDestination; break;
            case kVDSetupBuffersSelect:             vdigProc = (ComponentRoutineUPP)vdigSetupBuffers; break;
            case kVDGrabOneFrameAsyncSelect:        vdigProc = (ComponentRoutineUPP)vdigGrabOneFrameAsync; break;
            case kVDDoneSelect:                     vdigProc = (ComponentRoutineUPP)vdigDone; break;
            case kVDGetNumberOfInputsSelect:        vdigProc = (ComponentRoutineUPP)vdigGetNumberOfInputs; break;
            case kVDGetInputFormatSelect:           vdigProc = (ComponentRoutineUPP)vdigGetInputFormat; break;
            case kVDSetInputSelect:                 vdigProc = (ComponentRoutineUPP)vdigSetInput; break;
            case kVDGetInputSelect:                 vdigProc = (ComponentRoutineUPP)vdigGetInput; break;
            case kVDSetCompressionSelect:           vdigProc = (ComponentRoutineUPP)vdigSetCompression; break;
            case kVDCompressOneFrameAsyncSelect:    vdigProc = (ComponentRoutineUPP)vdigCompressOneFrameAsync; break;
            case kVDCompressDoneSelect:             vdigProc = (ComponentRoutineUPP)vdigCompressDone; break;
            case kVDReleaseCompressBufferSelect:    vdigProc = (ComponentRoutineUPP)vdigReleaseCompressBuffer; break;
            case kVDGetImageDescriptionSelect:      vdigProc = (ComponentRoutineUPP)vdigGetImageDescription; break;
            case kVDResetCompressSequenceSelect:    vdigProc = (ComponentRoutineUPP)vdigResetCompressSequence; break;
            case kVDSetCompressionOnOffSelect:      vdigProc = (ComponentRoutineUPP)vdigSetCompressionOnOff; break;
            case kVDGetCompressionTypesSelect:      vdigProc = (ComponentRoutineUPP)vdigGetCompressionTypes; break;
            case kVDSetTimeBaseSelect:              vdigProc = (ComponentRoutineUPP)vdigSetTimeBase; break;
            case kVDSetFrameRateSelect:             vdigProc = (ComponentRoutineUPP)vdigSetFrameRate; break;
            case kVDGetDMADepthsSelect:             vdigProc = (ComponentRoutineUPP)vdigGetDMADepths; break;
            case kVDGetPreferredTimeScaleSelect:    vdigProc = (ComponentRoutineUPP)vdigGetPreferredTimeScale; break;
 
            default:
                        err = digiUnimpErr;
                        break;
        }
    }
 
    if (vdigProc) {
        err = CallComponentFunctionWithStorage((Handle)storage, params, vdigProc);
        
    // kck - for debug only to catch calls that fail
    if (((params->what != kVDSetFrameRateSelect) && 
            (params->what != kVDCompressDoneSelect) && 
            (params->what != kVDGetMaxAuxBufferSelect) &&
            (params->what != kVDUseThisCLUTSelect) && 
            (params->what != kVDSetCompressionOnOffSelect)
            ) && err ) {
            return err;
        }
        return err;
    }
 
    return err;
}
 
#ifdef DEBUG_IT
Component RegisterSoftVdig(void)
{
    ComponentDescription foo;
    Component fooey;
    Handle name;
 
    foo.componentType = videoDigitizerComponentType;
    foo.componentSubType = 'soft';
    foo.componentManufacturer = 'jph ';
    foo.componentFlags = 0L;
    foo.componentFlagsMask = 0L;
 
    PtrToHand ((Ptr)"\psoftVdig (Linked)", &name, 18);
    fooey = RegisterComponent (&foo, (void *)softVdig, 0, name, 0, 0);
    DisposHandle (name);
    SetDefaultComponent (fooey, defaultComponentAnyFlagsAnyManufacturerAnySubType);
 
    return fooey;
}
#endif DEBUG_IT
 
pascal ComponentResult vdigOpen(vdigGlobals storage, ComponentInstance self)
{
    ComponentResult err;
 
    if (CountComponentInstances((Component)self) > (short)gMaxSoftVdigCount)
        return(-1);
 
    storage = (vdigGlobals)NewHandleClear(sizeof(vdigGlobalsRecord));
    if (err = MemError()) goto bail;
    (**storage).self = self;
    SetComponentInstanceStorage(self, (Handle)storage);
#if TARGET_OS_MAC
    SetComponentInstanceA5(self, *(long *)0x904);
#endif
 
    // Initialize some of the global storage members
    err = vdigGetMaxSrcRect(storage, ntscIn, &(**storage).maxSrcRect);
    (**storage).digiRect = (**storage).maxSrcRect;
    (**storage).pendingAsyncBuffer = -1;
    err = vdigGetVideoDefaults(storage,
                             &(**storage).blackLevel, &(**storage).whiteLevel,
                            &(**storage).brightness, &(**storage).hue, &(**storage).saturation,
                            &(**storage).contrast, &(**storage).sharpness);
 
// Initialize two additional softvdig configurable items
    (**storage).gAttachedGDevice = 0;
    MacSetRect(&(**storage).gAuxBufferRect,0,0,300,300);
 
 
bail:
    return(err);
}
 
pascal ComponentResult vdigClose(vdigGlobals storage, ComponentInstance self)
{
    if (storage) {
        if ((**storage).tempPort)
            DisposeGWorld((**storage).tempPort);
 
        if ((**storage).auxBuffer)
            DisposeGWorld((**storage).auxBuffer);
 
        if ((**storage).bufferList)
            DisposeHandle((Handle)(**storage).bufferList);
 
        if ((**storage).clipRgn)
            DisposeRgn((**storage).clipRgn);
 
        tossCompressStuff(storage);
 
        DisposeHandle((Handle)storage);
    }
 
    return noErr;
}
 
pascal ComponentResult vdigCanDo(vdigGlobals storage, short ftnNumber)
{
#define kvdigSelectors kVDSetPreferredPacketSizeSelect
    return (ftnNumber <= kvdigSelectors) && (ftnNumber >= kComponentVersionSelect);
}
 
pascal ComponentResult vdigVersion(vdigGlobals storage)
{
    return (vdigInterfaceRev<<16) | 1;
}
 
pascal VideoDigitizerError vdigGetMaxSrcRect(vdigGlobals storage, short inputStd, Rect *maxSrcRect)
{
long    error = noErr;
 
    if (inputStd == ntscIn)         // softvdig supports only NTSC
        MacSetRect(maxSrcRect, 0, 0, kMaxHorNTSCIn, kMaxVerNTSCIn + kVerBlank);
    else
        error = paramErr;
    if (!error) (**storage).maxSrcRect = *maxSrcRect;
 
    return error;
}
 
pascal VideoDigitizerError vdigGetActiveSrcRect(vdigGlobals storage, short inputStd, Rect *activeSrcRect)
{
    *activeSrcRect = (**storage).maxSrcRect;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigSetDigitizerRect(vdigGlobals storage, Rect *digiRect)
{
    Rect tempR;
 
    // can't be empty
    if (!digiRect || EmptyRect(digiRect))
        return(paramErr);
 
    // they must intersect
    if (!SectRect(digiRect, &(**storage).maxSrcRect, &tempR))
        return(paramErr);
 
    // completely...
    if (!MacEqualRect(digiRect, &tempR))
        return(paramErr);
 
    (**storage).digiRect = *digiRect;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigGetDigitizerRect(vdigGlobals storage, Rect *digiRect)
{
    *digiRect = (**storage).digiRect;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigUseThisCLUT(vdigGlobals storage, CTabHandle colorTableHandle)
{
    return digiUnimpErr;
}
 
void makeOffscreen(vdigGlobals storage)
{
    OSErr err;
    GWorldPtr tempPort;
    Rect r;
 
    tossOffscreen(storage);
 
    if (!(**storage).playThruPixMap) return;
 
    MacSetRect(&r, 0, 0, 4, 4);
    err = NewGWorld(&tempPort, (**(**storage).playThruPixMap).pixelSize, &r, 0, 0, 0);
    if (err) return;
 
    (**storage).tempPort = tempPort;
    MacSetRectRgn(tempPort->visRgn, -32000, -32000, 32000, 32000);
}
 
void tossOffscreen(vdigGlobals storage)
{
    if ((**storage).tempPort) {
        DisposeGWorld((**storage).tempPort);
        (**storage).tempPort = 0;
    }
}
 
void drawVideoFrame(vdigGlobals storage, Point where, PixMapHandle destPixMap)
{
    Rect destRect;
    CGrafPtr savePort;
    GDHandle saveGD;
    PixMapHandle savePortPix;
    Rect pictRect;
    Rect clipRect;
    Str255 tempStr;
    Point offsetClip;
    RgnHandle clipRgn;
    FontInfo    fontInfo;
    short       stringWidth, textSize;
 
    if (!(**storage).tempPort && !(**storage).compressGW) {
        makeOffscreen(storage);
        if (!(**storage).tempPort)
            return;
    }
 
    destRect = (**storage).digiRect;
    TransformRect(&(**storage).matrix, &destRect, 0);
    MacOffsetRect(&destRect, -destRect.left + where.h, -destRect.top + where.v);
 
    GetGWorld(&savePort, &saveGD);
    if ((**storage).compressGW)
        SetGWorld((**storage).compressGW, 0);
    else {
        SetGWorld((**storage).tempPort, 0);
        savePortPix = (**storage).tempPort->portPixMap;
        SetPortPix(destPixMap);
    }
 
    clipRect = (**storage).digiRect;
    ClipRect(&clipRect);
 
    if (clipRgn = (**storage).clipRgn) {
        offsetClip.h = where.h - (**storage).clipOrigin.h;
        offsetClip.v = where.v - (**storage).clipOrigin.v;
        MacOffsetRgn(clipRgn, offsetClip.h, offsetClip.v);
        SetClip(clipRgn);
    }
    else {
        MacSetRect(&clipRect, -32000, -32000, 32000, 32000);
        ClipRect(&clipRect);
    }
 
    clipRect = (**storage).maxSrcRect;
    MapRect(&clipRect, &(**storage).digiRect, &destRect);
 
    pictRect = clipRect;
    EraseRect(&pictRect);
    MacFrameRect(&pictRect);
    MacInsetRect(&pictRect, 10, 10);
    MacFrameRect(&pictRect);
    MacInsetRect(&pictRect, 10, 10);
    TextFont(kFontIDHelvetica);
    textSize = (pictRect.bottom - pictRect.top) >> 1;
    TextSize(textSize);
    NumToString((**storage).frameNumber, tempStr);
    stringWidth = StringWidth(tempStr);
    while (stringWidth > (pictRect.right - pictRect.left))
    {
        textSize /= 2;
        TextSize(textSize);
        NumToString((**storage).frameNumber, tempStr);
        stringWidth = StringWidth(tempStr);
    }
    GetFontInfo(&fontInfo);
    MoveTo((pictRect.left + pictRect.right)/2 - stringWidth/2,
        (pictRect.top + pictRect.bottom)/2 - (fontInfo.ascent+fontInfo.descent)/2 + fontInfo.ascent);
    DrawString(tempStr);
 
    (**storage).frameNumber++;
 
    if (clipRgn) {
        MacOffsetRgn(clipRgn, -offsetClip.h, -offsetClip.v);
        MacSetRect(&clipRect, -32000, -32000, 32000, 32000);
        ClipRect(&clipRect);
    }
 
    if (!(**storage).compressGW)
        SetPortPix(savePortPix);
    SetGWorld(savePort, saveGD);
}
 
pascal VideoDigitizerError vdigGrabOneFrame(vdigGlobals storage)
{
    if (!(**storage).playThruPixMap)
        return badCallOrderErr;
 
    drawVideoFrame(storage, *(Point *)&((**storage).playThruRect), (**storage).playThruPixMap);
 
    return noErr;
}
pascal VideoDigitizerError vdigSetContrast(vdigGlobals storage, unsigned short *contrast)
{
    (**storage).contrast = *contrast;
    return noErr;
}
 
pascal VideoDigitizerError vdigGetContrast(vdigGlobals storage, unsigned short *contrast)
{
    *contrast = (**storage).contrast;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetHue(vdigGlobals storage, unsigned short *hue) {
    (**storage).hue = *hue;
    return noErr;
}
pascal VideoDigitizerError vdigGetHue(vdigGlobals storage, unsigned short *hue)
{
    *hue = (**storage).hue;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetBrightness(vdigGlobals storage, unsigned short *brightness)
{
    (**storage).brightness = *brightness;
    return noErr;
}
pascal VideoDigitizerError vdigGetBrightness(vdigGlobals storage, unsigned short *brightness)
{
    *brightness = (**storage).brightness;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetSaturation(vdigGlobals storage, unsigned short *saturation)
{
    (**storage).saturation = *saturation;
    return noErr;
}
 
pascal VideoDigitizerError vdigGetSaturation(vdigGlobals storage, unsigned short *saturation)
{
    *saturation = (**storage).saturation;
    return noErr;
}
pascal VideoDigitizerError vdigSetSharpness(vdigGlobals storage, unsigned short *sharpness)
{
    (**storage).sharpness = *sharpness;
    return noErr;
}
 
pascal VideoDigitizerError vdigGetSharpness(vdigGlobals storage, unsigned short *sharpness)
{
    *sharpness = (**storage).sharpness;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetBlackLevel(vdigGlobals storage, unsigned short *blackLevel)
{
    (**storage).blackLevel = *blackLevel;
    return noErr;
}
pascal VideoDigitizerError vdigGetBlackLevel(vdigGlobals storage, unsigned short *blackLevel)
{
    *blackLevel = (**storage).blackLevel;
    return noErr;
}
pascal VideoDigitizerError vdigSetWhiteLevel(vdigGlobals storage, unsigned short *whiteLevel)
{
    (**storage).whiteLevel = *whiteLevel;
    return noErr;
}
pascal VideoDigitizerError vdigGetWhiteLevel(vdigGlobals storage, unsigned short *whiteLevel){
    *whiteLevel = (**storage).whiteLevel;
    return noErr;
}
pascal VideoDigitizerError vdigGetVideoDefaults(vdigGlobals storage,
                            unsigned short *blackLevel, unsigned short *whiteLevel,
                            unsigned short *brightness, unsigned short *hue, unsigned short *saturation,
                            unsigned short *contrast, unsigned short *sharpness)
{
    (**storage).blackLevel  = 0;
    (**storage).whiteLevel  = 60000;
    (**storage).brightness  = 20000;
    (**storage).hue         = 30000;
    (**storage).saturation  = 40000;
    (**storage).contrast    = 50000;
    (**storage).sharpness   = 60000;
    return noErr;
}
 
pascal VideoDigitizerError vdigGetMaxAuxBuffer(vdigGlobals storage, PixMapHandle *pm, Rect *r)
{
    OSErr   err = noErr;
 
    if (!gHasAuxBuffer)
        return digiUnimpErr;
 
    if (!(**storage).auxBuffer) {
        GWorldPtr tempGW;
 
        err = NewGWorld(&tempGW, gAuxDepth, &(**storage).gAuxBufferRect, 0, 0, 0);
        if (err) goto bail;
 
        LockPixels(tempGW->portPixMap);
 
        (**storage).auxBuffer = tempGW;
    }
 
    if (pm) *pm = (**storage).auxBuffer->portPixMap;
    if (r) *r = (**(**storage).auxBuffer->portPixMap).bounds;
 
bail:
    return err;
}
 
pascal VideoDigitizerError vdigGetDigitizerInfo(vdigGlobals storage, DigitizerInfo *info)
{
    
    info->vdigType = gCanClip ? vdTypeMask : vdTypeBasic;
    info->inputCapabilityFlags = digiInDoesNTSC | digiInDoesComposite | digiInDoesColor;
    info->outputCapabilityFlags = gDoesDepths |
                                (gCanScale ? digiOutDoesStretch | digiOutDoesShrink : digiOutDoesQuarter | digiOutDoesSixteenth) | 
                                (gCanClip ? digiOutDoesMask : 0) |
                                (gCanDMA ? digiOutDoesHW_DMA : 0) |
                                (gDoesPlaythru ? digiOutDoesHWPlayThru : 0) |
                                (gCanAsync ? digiOutDoesAsyncGrabs : 0) |
                                (gDoesCompress ? digiOutDoesCompress : 0) |
                                ((gDoesCompress && gOnlyDoesCompress) ? digiOutDoesCompressOnly : 0)
                                ;
 
    info->inputCurrentFlags = info->inputCapabilityFlags;
    info->inputCurrentFlags |= digiInSignalLock;
    info->outputCurrentFlags = 0;
    if ((**storage).gAttachedGDevice)
        info->outputCurrentFlags = (**(**(**storage).gAttachedGDevice).gdPMap).pixelSize;
    else {
 
    }
 
    info->slot = 0;                             // we have no slot
    info->gdh = (**storage).gAttachedGDevice;
    info->maskgdh = 0;                          
    info->minDestHeight = gMinHeight;
    info->minDestWidth = gMinWidth;
    info->maxDestHeight = gMaxHeight;
    info->maxDestWidth = gMaxWidth;
    info->blendLevels = 0;
    info->reserved = 0;
    
    (**storage).inputCurrentFlags  = info->inputCurrentFlags;
    (**storage).outputCurrentFlags = info->outputCurrentFlags;
    
    return noErr;
}
pascal VideoDigitizerError  vdigGetCurrentFlags(vdigGlobals storage, long *inputCurrentFlag, long *outputCurrentFlag) 
{
    *inputCurrentFlag = (**storage).inputCurrentFlags;
    *outputCurrentFlag = (**storage).outputCurrentFlags;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetPlayThruOnOff(vdigGlobals storage, short state)
{
    if (!gDoesPlaythru)
        return digiUnimpErr;
 
    (**storage).playThruOn = (state != 0);
    return noErr;
}
 
pascal VideoDigitizerError vdigSetPlayThruDestination(vdigGlobals storage, PixMapHandle dest, Rect *dr, MatrixRecord *m, RgnHandle mask)
{
    OSErr err;
    short width, height;
    Rect destRect;
 
    if (!dest || (!dr && !m))
        return paramErr;
 
    if (dr)
        destRect = *dr;
    else {
        if (GetMatrixType(m) > scaleTranslateMatrixType)
            return matrixErr;
        destRect = (**storage).digiRect;
        TransformRect(m, &destRect, nil);
    }
 
    // make sure the rectangle is ok
    if (EmptyRect(&destRect))
        return paramErr;
 
    if (mask && !gCanClip)
        return paramErr;
 
    if (!validatePixMap(storage, dest))
        return paramErr;
 
    if ((**storage).clipRgn) {
        DisposeRgn((**storage).clipRgn);
        (**storage).clipRgn = 0;
    }
 
    if (mask) {
        HandToHand((Handle *)&mask);
        if (err = MemError()) return err;
    }
    (**storage).clipRgn = mask;
    (**storage).clipOrigin = *(Point *)&destRect;
 
    {
    Rect tempRect;
 
    tempRect = destRect;
    MapRect(&tempRect, &(**storage).digiRect, &(**storage).maxSrcRect);         // convert to full frame
    RectMatrix(&(**storage).matrix, &(**storage).maxSrcRect, &tempRect);        // make a matrix
 
    tempRect = (**storage).maxSrcRect;
    TransformRect(&(**storage).matrix, &tempRect, 0);
    width = tempRect.right - tempRect.left;
    height = tempRect.bottom - tempRect.top;
    }
 
    if (width < gMinWidth || width > gMaxWidth || height < gMinHeight || height > gMaxHeight)
        return paramErr;
 
#if 0
    if (width != gMaxWidth || height != gMaxHeight)
        return paramErr;
#endif
 
    if (!gCanScale) {
        short i = 3;
        short tempWidth = gMaxWidth;
        short tempHeight = gMaxHeight;
        Boolean ok;
 
        while (i--) {
            ok = (width == tempWidth) && (height == tempHeight);
            if (ok) break;
            tempWidth >>= 1;
            tempHeight >>= 1;
        }
        if (!ok)
            return paramErr;
    }
 
    if ((**storage).tempPort) {
        if ((**dest).pixelSize != (**(**storage).tempPort->portPixMap).pixelSize)
            tossOffscreen(storage);
    }
 
    (**storage).playThruRect = destRect;
    (**storage).playThruPixMap = dest;
 
    return noErr;
}
 
// better checks for pixmap baseAddr matching could be done
Boolean validatePixMap(vdigGlobals storage, PixMapHandle p)
{
    if ( !((**p).pixelSize & gDoesDepths) ) {
        if (gCanDMA && ((**p).pixelSize & gDMADepths))
            ;
        else
            return false;
    }
 
    if (gCanDMA)                        // any other place is OK
        return true;
 
    if ((**storage).gAttachedGDevice) {
        // see if pixels are on the device
        if ((**p).baseAddr == (**(**(**storage).gAttachedGDevice).gdPMap).baseAddr)
            return true;
    }
 
    if ((**storage).auxBuffer) {
        if ((**p).baseAddr == (**(**storage).auxBuffer->portPixMap).baseAddr)
            return true;
    }
 
    if (!(**storage).gAttachedGDevice) return true;
 
    return false;
}
 
pascal VideoDigitizerError vdigPreflightDestination(vdigGlobals storage, Rect *digitizerRect, PixMap **dest, Rect *destRect, MatrixRecord *m)
{
    //¥¥  real code is required here in your vdig!!
 
    return noErr;
}
 
pascal VideoDigitizerError vdigSetupBuffers(vdigGlobals storage, VdigBufferRecListHandle bufferList)
{
    OSErr err;
    MatrixRecord matrix;
    short i;
    RgnHandle clipRgn;
 
    if (!bufferList)
        return paramErr;
 
    if (!(**bufferList).count)
        return paramErr;
 
    if ((**storage).bufferList) {
        DisposeHandle((Handle)(**storage).bufferList);
        (**storage).bufferList = 0;
    }
 
    if ((**storage).clipRgn) {
        DisposeRgn((**storage).clipRgn);
        (**storage).clipRgn = 0;
    }
 
    if (!gCanClip && (**bufferList).mask)
        return paramErr;
 
    if ((**bufferList).matrix)
        matrix = *(**bufferList).matrix;
 
    // make a local copy of the buffer list
    HandToHand((Handle *)&bufferList);
    if (err = MemError()) return err;
 
    if (clipRgn = (**bufferList).mask) {
        HandToHand((Handle *)&clipRgn);
        if (err = MemError()) return err;
        (**storage).clipOrigin = (**bufferList).list[0].location;
    }
 
    (**storage).bufferList = bufferList;
    (**storage).clipRgn = clipRgn;
    (**storage).pendingAsyncBuffer = -1;
    (**storage).matrix = matrix;
 
    for (i=0; i < (**bufferList).count; i++) {  
        if (!validatePixMap(storage, (**bufferList).list[i].dest))
            return paramErr;
        // should check the point here too
    }
 
    return noErr;
}
 
pascal VideoDigitizerError vdigGrabOneFrameAsync(vdigGlobals storage, short buffer)
{
    VdigBufferRecListHandle bufferList;
 
    if (!gCanAsync)
        return digiUnimpErr;
 
    if (!(bufferList = (**storage).bufferList))
        return badCallOrderErr;
 
    if (buffer > (**bufferList).count)
        return paramErr;
 
    if ((**storage).pendingAsyncBuffer != -1) {
        short aBuf = (**storage).pendingAsyncBuffer;
 
#if HAVE_DEBUGSTR
        if (aBuf == buffer)
            DebugStr("\pasync grab into incomplete buffer");
#endif
 
        drawVideoFrame(storage, (**bufferList).list[aBuf].location, (**bufferList).list[aBuf].dest);
    }
 
    (**storage).pendingAsyncBuffer = buffer;
 
    return noErr;
}
 
pascal long vdigDone(vdigGlobals storage, short buffer)
{
    vdigGlobalsPtr store = *storage;
    VdigBufferRecListHandle bufferList;
 
    if (!gCanAsync)
        return 0;
 
    if (!(bufferList = store->bufferList))
        return 0;
 
    if (buffer > (**bufferList).count)
        return 0;
 
    if (store->frameTimeStep && GetTimeBaseRate(store->timeBase)) {
        if (GetTimeBaseTime(store->timeBase, gVideoTimeScale ? gVideoTimeScale : 600, nil) >= store->nextFrameTime)
            store->nextFrameTime += store->frameTimeStep;
        else
            return false;
    }
 
    if (store->pendingAsyncBuffer == buffer) {
        drawVideoFrame(storage, (**bufferList).list[buffer].location, (**bufferList).list[buffer].dest);
        store->pendingAsyncBuffer = -1;
    }
 
    return true;
}
 
/*
    inputs garbage
*/
 
pascal VideoDigitizerError vdigGetNumberOfInputs(vdigGlobals storage, short *inputs)
{
    *inputs = 0;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigGetInputFormat(vdigGlobals storage, short input, short *format)
{
    if (input == 0) {
        *format = ntscIn;
        return noErr;
    }
    else
        return paramErr;
}
 
pascal VideoDigitizerError vdigSetInput(vdigGlobals storage, short input)
{
    if (input == 0)
        return noErr;
    else
        return paramErr;
}
 
pascal VideoDigitizerError vdigGetInput(vdigGlobals storage, short *input)
{
    *input = 0;
 
    return noErr;
}
 
/*
    compressed source stuff
*/
 
void tossCompressStuff(vdigGlobals storage)
{
    if ((**storage).compressSeq) {
        CDSequenceEnd((**storage).compressSeq);
        (**storage).compressSeq = 0;
    }
 
    if ((**storage).compressGW) {
        DisposeGWorld((**storage).compressGW);
        (**storage).compressGW = 0;
    }
 
    if ((**storage).compressBuffer) {
        DisposePtr((**storage).compressBuffer);
        (**storage).compressBuffer = 0;
    }
 
    if ((**storage).desc) {
        DisposeHandle((Handle)(**storage).desc);
        (**storage).desc = 0;
    }
}
 
 
pascal VideoDigitizerError vdigSetCompression(vdigGlobals storage, OSType compressType, short depth, Rect *bounds,
            CodecQ spatialQuality, CodecQ temporalQuality, long keyFrameRate)
{
    OSErr err;
    GWorldPtr gw = 0;
    long bufSize;
    Ptr p;
    ImageDescription ** tempImageDesc;
    ImageSequence tempSeqId;
 
    if (!gDoesCompress)
        return digiUnimpErr;
 
    tossCompressStuff(storage);
 
    if (compressType == 'vpza')
        compressType = 'rpza';
 
    // see if we handle this compression
    if (compressType && gOnlyCompressType && 
        (compressType != gOnlyCompressType))
        return noCodecErr;
 
    if (!compressType) {
        compressType = gOnlyCompressType ? gOnlyCompressType : 'rpza';
        spatialQuality = codecNormalQuality;
        temporalQuality = 0;
        keyFrameRate = 0;
    }
 
    (**storage).compressType = compressType;
    (**storage).compressDepth = depth ? depth : 16;
    (**storage).compressRect = *bounds;
    (**storage).spatialQuality = spatialQuality;
    (**storage).temporalQuality = temporalQuality;
    (**storage).keyFrameRate = keyFrameRate;
 
    if (compressType == -1)
        return noErr;
 
    {
    Rect tempRect;
 
    tempRect = *bounds;
    MapRect(&tempRect, &(**storage).digiRect, &(**storage).maxSrcRect);         // convert to full frame
    RectMatrix(&(**storage).matrix, &(**storage).maxSrcRect, &tempRect);        // make a matrix
    }
 
    err = NewGWorld(&gw, depth, bounds, 0, 0, 0);
    if (err) goto bail;
    (**storage).compressGW = gw;
    LockPixels(gw->portPixMap);
 
    err = GetMaxCompressionSize(gw->portPixMap, bounds, depth, spatialQuality, compressType, bestSpeedCodec, &bufSize);
    if (err) goto bail;
 
    p = NewPtr(bufSize);
    if (err = MemError())  goto bail;
    (**storage).compressBuffer = p;
 
    tempImageDesc = (ImageDescription **)NewHandle(sizeof(ImageDescription));
    if (err = MemError()) goto bail;
    (**storage).desc = tempImageDesc;
 
    // fill this out for now.. mark will help us out later
    (**tempImageDesc).temporalQuality = temporalQuality;
    (**tempImageDesc).spatialQuality = spatialQuality;
    (**tempImageDesc).width = bounds->right - bounds->left;
    (**tempImageDesc).height = bounds->bottom - bounds->top;
    (**tempImageDesc).depth = (compressType == 'rpza') ? 16 : depth;
    (**tempImageDesc).cType = compressType;
    (**tempImageDesc).hRes = 0x00480000;
    (**tempImageDesc).vRes = 0x00480000;
 
    err = CompressSequenceBegin(&tempSeqId,
                    gw->portPixMap, (PixMap **)0,
                    bounds, (Rect *)0,
                    depth, compressType, bestSpeedCodec, spatialQuality, 
                    temporalQuality, keyFrameRate, (CTabHandle )0,
                    codecFlagUpdatePrevious, tempImageDesc );
    if (err) goto bail;
 
    (**storage).compressSeq = tempSeqId;
 
bail:
    if (err)
        tossCompressStuff(storage);
 
    return err;
}
 
pascal VideoDigitizerError vdigCompressOneFrameAsync(vdigGlobals storage)
{
    if (!gDoesCompress)
        return digiUnimpErr;
 
    if (!(**storage).compressSeq)
        return badCallOrderErr;
 
    // draw the offscreen now, compress later...
    drawVideoFrame(storage, *(Point *)&(**storage).compressRect, (**storage).compressGW->portPixMap);
 
    return noErr;
}
 
pascal VideoDigitizerError vdigCompressDone(vdigGlobals storage, Boolean *done, Ptr *theData, long *dataSize, unsigned char *similarity, TimeRecord *tr)
{
    vdigGlobalsPtr store = *storage;
    OSErr err;
    Rect r;
 
    *done = true;
 
    if (!gDoesCompress)
        return digiUnimpErr;
 
    if (!store->compressSeq)
        return badCallOrderErr;
 
    if (store->frameTimeStep && GetTimeBaseRate(store->timeBase)) {
        if (GetTimeBaseTime(store->timeBase, gVideoTimeScale ? gVideoTimeScale : 600, nil) >= store->nextFrameTime)
            store->nextFrameTime += store->frameTimeStep;
        else {
            *done = false;
            return noErr;
        }
    }
 
    r = store->compressRect;
/*
extern pascal OSErr CompressSequenceFrame(ImageSequence seqID, PixMapHandle src, const Rect *srcRect, 
                                          CodecFlags flags, Ptr data, long *dataSize, UInt8 *similarity, 
                                          ICMCompletionProcRecordPtr asyncCompletionProc)
*/
    err = (OSErr)CompressSequenceFrame(store->compressSeq, store->compressGW->portPixMap, &r,
                            codecFlagUpdatePrevious | (store->forceKeyFrame ? codecFlagForceKeyFrame : 0),
                            store->compressBuffer, 
                            dataSize, 
                            similarity, 
                            (ICMCompletionProcRecordPtr)0);
    store = *storage;
    *theData = store->compressBuffer;
 
    store->forceKeyFrame = false;
 
    GetTimeBaseTime(store->timeBase, 30, tr);
 
    return err;
}
 
pascal VideoDigitizerError vdigReleaseCompressBuffer(vdigGlobals storage, Ptr theBufffer)
{
    if (!gDoesCompress)
        return digiUnimpErr;
 
    if (!(**storage).compressSeq)
        return badCallOrderErr;
 
    //¥¥ could actually track this...
    //¥¥Êcould pound part of buffer to 0 or something
 
    return noErr;
}
 
 
pascal VideoDigitizerError vdigGetImageDescription(vdigGlobals storage, ImageDescriptionHandle desc)
{
    OSErr err = noErr;
 
    if (!gDoesCompress)
        return digiUnimpErr;
 
    if ((**storage).desc) {
        long dataSize;
 
        SetHandleSize((Handle)desc, dataSize = GetHandleSize((Handle)(**storage).desc));
        if (err = MemError()) goto bail;
        BlockMove((Ptr)*(**storage).desc, (Ptr)*desc, dataSize);
    }
    else
        err = badCallOrderErr;
 
bail:
    return err;
}
 
pascal VideoDigitizerError vdigResetCompressSequence(vdigGlobals storage)
{
    OSErr err = noErr;
 
    if (!gDoesCompress)
        return digiUnimpErr;
 
    (**storage).forceKeyFrame = true;
 
    return err;
}
 
pascal VideoDigitizerError vdigSetCompressionOnOff(vdigGlobals storage, Boolean newState)
{
    OSErr err = noErr;
 
    if (!gDoesCompress)
        return digiUnimpErr;
 
    if (newState != (**storage).compressOn) {
        (**storage).compressOn = newState;
        if (!(**storage).compressOn)
            tossCompressStuff(storage);
    }
 
    return err;
}
 
pascal VideoDigitizerError vdigGetCompressionTypes(vdigGlobals storage, VDCompressionListHandle h)
{
    CodecInfo info;
    VDCompressionListPtr p;
 
    GetCodecInfo(&info, 'rpza',  0);
    SetHandleSize((Handle)h, sizeof(VDCompressionList));
    p = &(*h)[0];
    BlockMove(info.typeName, p->typeName, 32);
    p->typeName[++p->typeName[0]] = ' ';
    p->typeName[++p->typeName[0]] = 'V';
    p->typeName[++p->typeName[0]] = 'D';
    p->typeName[++p->typeName[0]] = 'I';
    p->typeName[++p->typeName[0]] = 'G';
    BlockMove(p->typeName, p->name, 64);
    p->codec = 0;
    p->cType = 'rpza';
    p->formatFlags = info.formatFlags;
    p->compressFlags = info.compressFlags;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigSetTimeBase(vdigGlobals storage, TimeBase t)
{
    (**storage).timeBase = t;
    return noErr;
}
 
pascal VideoDigitizerError vdigSetFrameRate(vdigGlobals storage, Fixed framesPerSecond)
{
    vdigGlobalsPtr store = *storage;
 
    if (!gDoesFrameRate)
        return digiUnimpErr;
 
    if (framesPerSecond < 0)
        return paramErr;
 
    store->nextFrameTime = store->frameTimeStep = 0;
    if (framesPerSecond)
        store->frameTimeStep = FixDiv(gVideoTimeScale ? gVideoTimeScale : 600, framesPerSecond);
 
    return noErr;
}
 
pascal VideoDigitizerError vdigGetDMADepths(vdigGlobals storage, long *depthArray, long *preferredDepth)
{
    if (!gCanDMA)
        return paramErr;
 
    *depthArray = gDMADepths;
    *preferredDepth = 40;
 
    return noErr;
}
 
pascal VideoDigitizerError vdigGetPreferredTimeScale(vdigGlobals storage, TimeScale *preferred)
{
    if (gVideoTimeScale) {
        *preferred = gVideoTimeScale;
        return noErr;
    }
    else
        return badComponentSelector;
}