Sample Components/SGChannelPict.c

/*
    File:       SGChannelPict.r
 
    Written by: Peter Hoddie
 
    Copyright:  © 1992 by Apple Computer, Inc., all rights reserved.
 
*/
 
#include <Errors.h>
#include <Memory.h>
#include <OSUtils.h>
#include <Packages.h>
#include <QuickDraw.h>
#include <Resources.h>
 
#include <QuickTimeComponents.h>
 
#define kMediaTimeScale 600
 
typedef struct {
    ComponentInstance       self;
    SeqGrabComponent        grabber;
 
    long                    usage;
    Boolean                 paused;
 
    CGrafPtr                destPort;
    GDHandle                destGD;
 
    CGrafPtr                tempPort;
 
    MatrixRecord            displayMatrix;
    Rect                    destRect;
 
    Rect                    srcRect;
 
    RgnHandle               clip;
 
    Boolean                 inPreview;
    Boolean                 inRecord;
 
    TimeBase                base;
 
    long                    bytesWritten;
 
    Boolean                 showTickCount;
 
    long                    saveUsage;
} SGPictGlobalsRecord, *SGPictGlobals;
 
pascal ComponentResult SGPictOpen (SGPictGlobals store, ComponentInstance self);
pascal ComponentResult SGPictClose (SGPictGlobals store, ComponentInstance self);
pascal ComponentResult SGPictCanDo (SGPictGlobals store, short ftnNumber );
pascal ComponentResult SGPictVersion (SGPictGlobals store);
 
pascal ComponentResult SGPictSetGWorld (SGPictGlobals store, CGrafPtr gp, GDHandle gd);
pascal ComponentResult SGPictSetChannelUsage (SGPictGlobals store, long usage);
pascal ComponentResult SGPictGetChannelUsage (SGPictGlobals store, long *usage);
pascal ComponentResult SGPictGetChannelInfo (SGPictGlobals store, long *channelInfo);
 
pascal ComponentResult SGPictSetChannelMatrix (SGPictGlobals store, const MatrixRecord *m);
pascal ComponentResult SGPictGetChannelMatrix (SGPictGlobals store, MatrixRecord *m);
pascal ComponentResult SGPictSetChannelBounds (SGPictGlobals store, const Rect *bounds);
pascal ComponentResult SGPictGetChannelBounds (SGPictGlobals store, Rect *bounds);
pascal ComponentResult SGPictSetChannelClip (SGPictGlobals store, RgnHandle theClip);
pascal ComponentResult SGPictGetChannelClip (SGPictGlobals store, RgnHandle *theClip);
 
pascal ComponentResult SGPictStartPreview (SGPictGlobals store);
pascal ComponentResult SGPictStartRecord (SGPictGlobals store);
pascal ComponentResult SGPictIdle (SGPictGlobals store);
pascal ComponentResult SGPictStop (SGPictGlobals store);
pascal ComponentResult SGPictPause (SGPictGlobals store, Byte pause);
pascal ComponentResult SGPictPrepare (SGPictGlobals store, Boolean prepareForPreview, 
                Boolean prepareForRecord);
pascal ComponentResult SGPictRelease (SGPictGlobals store);
 
pascal ComponentResult SGPictGetChannelSampleDescription (SGPictGlobals store, Handle sampleDesc);
pascal ComponentResult SGPictGetChannelDeviceList (SGPictGlobals store, long selectionFlags, 
                SGDeviceList *list);
pascal ComponentResult SGPictSetChannelDevice (SGPictGlobals store, StringPtr name);
pascal ComponentResult SGPictGetChannelTimeScale (SGPictGlobals store, TimeScale *scale);
pascal ComponentResult SGPictInitChannel (SGPictGlobals store, SeqGrabComponent owner);
pascal ComponentResult SGPictWriteSamples (SGPictGlobals store, Movie m, AliasHandle theFile);
pascal ComponentResult SGPictGetDataRate (SGPictGlobals store, long *bytesPerSecond);
 
pascal ComponentResult SGPictPanelGetDitl (SGPictGlobals store, Handle *ditl);
pascal ComponentResult SGPictPanelInstall (SGPictGlobals store, SGChannel c, DialogPtr d, short itemOffset);
pascal ComponentResult SGPictPanelEvent (SGPictGlobals store, SGChannel c, DialogPtr d, 
                short itemOffset, EventRecord *theEvent, short *itemHit, Boolean *handled);
pascal ComponentResult SGPictPanelRemove (SGPictGlobals store, SGChannel c, DialogPtr d, short itemOffset);
pascal ComponentResult SGPictPanelGetSettings (SGPictGlobals store, SGChannel c, UserData *ud, long flags);
pascal ComponentResult SGPictPanelSetSettings (SGPictGlobals store, SGChannel c, UserData ud, long flags);
 
pascal ComponentResult SGPictSetShowTickCount (SGPictGlobals store, Boolean show);
pascal ComponentResult SGPictGetShowTickCount (SGPictGlobals store, Boolean *show);
 
pascal ComponentResult SGPICTDispatcher(ComponentParameters *params, Handle storage);
 
/************************
    Component Management
************************/
 
pascal ComponentResult SGPICTDispatcher(ComponentParameters *params, Handle storage)
{
    OSErr err = badComponentSelector;
    ComponentFunction componentProc = 0;
 
    switch (params->what) {
        case kComponentOpenSelect: componentProc = SGPictOpen; break;
        case kComponentCloseSelect: componentProc = SGPictClose; break;
        case kComponentCanDoSelect: componentProc = SGPictCanDo; break;
        case kComponentVersionSelect: componentProc = SGPictVersion; break;
 
        case kSGSetGWorldSelect: componentProc = SGPictSetGWorld; break;
 
        case kSGStartPreviewSelect: componentProc = SGPictStartPreview; break;
        case kSGStartRecordSelect: componentProc = SGPictStartRecord; break;
        case kSGIdleSelect: componentProc = SGPictIdle; break;
        case kSGStopSelect: componentProc = SGPictStop; break;
        case kSGPauseSelect: componentProc = SGPictPause; break;
        case kSGPrepareSelect: componentProc = SGPictPrepare; break;
        case kSGReleaseSelect: componentProc = SGPictRelease; break;
 
        case kSGCSetChannelUsageSelect: componentProc = SGPictSetChannelUsage; break;
        case kSGCGetChannelUsageSelect: componentProc = SGPictGetChannelUsage; break;
        case kSGCSetChannelBoundsSelect: componentProc = SGPictSetChannelBounds; break;
        case kSGCGetChannelBoundsSelect: componentProc = SGPictGetChannelBounds; break;
        case kSGCGetChannelInfoSelect: componentProc = SGPictGetChannelInfo; break;
 
        case kSGCSetChannelMatrixSelect: componentProc = SGPictSetChannelMatrix; break;
        case kSGCGetChannelMatrixSelect: componentProc = SGPictGetChannelMatrix; break;
        case kSGCSetChannelClipSelect: componentProc = SGPictSetChannelClip; break;
        case kSGCGetChannelClipSelect: componentProc = SGPictGetChannelClip; break;
 
        case kSGCGetChannelSampleDescriptionSelect: componentProc = SGPictGetChannelSampleDescription; break;
        case kSGCGetChannelDeviceListSelect: componentProc = SGPictGetChannelDeviceList; break;
        case kSGCSetChannelDeviceSelect: componentProc = SGPictSetChannelDevice; break;
        case kSGCGetChannelTimeScaleSelect: componentProc = SGPictGetChannelTimeScale; break;
 
        case kSGCInitChannelSelect: componentProc = SGPictInitChannel; break;
        case kSGCWriteSamplesSelect: componentProc = SGPictWriteSamples; break;
        case kSGCGetDataRateSelect: componentProc = SGPictGetDataRate; break;
 
        case kSGCPanelGetDitlSelect: componentProc = SGPictPanelGetDitl; break;
        case kSGCPanelInstallSelect: componentProc = SGPictPanelInstall; break;
        case kSGCPanelEventSelect: componentProc = SGPictPanelEvent; break;
        case kSGCPanelRemoveSelect: componentProc = SGPictPanelRemove; break;
        case kSGCPanelGetSettingsSelect: componentProc = SGPictPanelGetSettings; break;
        case kSGCPanelSetSettingsSelect: componentProc = SGPictPanelSetSettings; break;
 
        // Private component calls
        case 0x0100: componentProc = SGPictSetShowTickCount ;break;
        case 0x0101: componentProc = SGPictGetShowTickCount ;break;
    }
 
    if (componentProc)
        err = CallComponentFunctionWithStorage(storage, params, componentProc);
 
    return err;
}
 
pascal ComponentResult SGPictCanDo (SGPictGlobals store, short ftnNumber )
{
    switch (ftnNumber) {
        case kComponentOpenSelect:
        case kComponentCloseSelect:
        case kComponentCanDoSelect:
        case kComponentVersionSelect:
        
        case kSGSetGWorldSelect: 
 
        case kSGStartPreviewSelect:
        case kSGStartRecordSelect: 
        case kSGIdleSelect: 
        case kSGStopSelect: 
        case kSGPauseSelect: 
        case kSGPrepareSelect: 
        case kSGReleaseSelect: 
 
        case kSGCSetChannelUsageSelect: 
        case kSGCGetChannelUsageSelect: 
        case kSGCSetChannelBoundsSelect: 
        case kSGCGetChannelBoundsSelect: 
        case kSGCGetChannelInfoSelect: 
 
        case kSGCSetChannelMatrixSelect: 
        case kSGCGetChannelMatrixSelect: 
        case kSGCSetChannelClipSelect: 
        case kSGCGetChannelClipSelect: 
 
        case kSGCGetChannelSampleDescriptionSelect: 
        case kSGCGetChannelDeviceListSelect: 
        case kSGCSetChannelDeviceSelect: 
        case kSGCGetChannelTimeScaleSelect: 
 
        case kSGCInitChannelSelect: 
        case kSGCWriteSamplesSelect: 
        case kSGCGetDataRateSelect: 
 
        case kSGCPanelGetDitlSelect: 
        case kSGCPanelInstallSelect: 
        case kSGCPanelEventSelect: 
        case kSGCPanelRemoveSelect: 
        case kSGCPanelGetSettingsSelect: 
        case kSGCPanelSetSettingsSelect: 
 
        // Private component calls
        case 0x0100: 
        case 0x0101: 
            return true;
        default:
            return false;
    }
}
 
pascal ComponentResult SGPictVersion (SGPictGlobals store)
{
    return 0x00020001;
}
 
pascal ComponentResult SGPictOpen (SGPictGlobals store, ComponentInstance self)
{
    OSErr err;
    Rect r;
    CGrafPtr tempPort;
    GrafPtr savePort;
 
    // allocate globals
    store = (SGPictGlobals)NewPtrClear(sizeof(SGPictGlobalsRecord));
    if (err = MemError()) goto bail;
 
    // create a temporary port for drawing to in the idle routine
    tempPort = (CGrafPtr)NewPtr(sizeof(CGrafPort));
    if (err = MemError()) {
        DisposePtr((Ptr)store);
        goto bail;
    }
    GetPort(&savePort);
    OpenCPort(tempPort);
    SetPort((GrafPtr)tempPort);
    PortSize(4096, 4096);       // randomly big port
    SetRect(&r, 0, 0, 4096, 4096);
    RectRgn(tempPort->visRgn, &r);
    ClipRect(&r);
    SetPort(savePort);
 
    store->self = self;
    store->tempPort = tempPort;
    store->showTickCount = false;
    SetComponentInstanceStorage(self, (Handle)store);
 
bail:
    return err;
}
 
pascal ComponentResult SGPictClose (SGPictGlobals store, ComponentInstance self)
{
    if (store) {
        if (store->clip) DisposeRgn(store->clip);
        if (store->tempPort) {
            CloseCPort(store->tempPort);
            DisposePtr((Ptr)store->tempPort);
        }
        DisposPtr((Ptr)store);
    }
 
    return noErr;
}
 
/************************
    Required SG Channel Calls
************************/
 
pascal ComponentResult SGPictSetGWorld (SGPictGlobals store, CGrafPtr gp, GDHandle gd)
{
// remember the destination GWorld
    store->destPort = gp;
    store->destGD = gd;
 
    return noErr;
}
 
pascal ComponentResult SGPictSetChannelUsage (SGPictGlobals store, long usage)
{
    store->usage = usage;
 
    return noErr;
}
 
pascal ComponentResult SGPictGetChannelUsage (SGPictGlobals store, long *usage)
{
    *usage = store->usage;
 
    return noErr;
}
 
pascal ComponentResult SGPictGetChannelInfo (SGPictGlobals store, long *channelInfo)
{
    *channelInfo = seqGrabHasBounds;
 
    return noErr;
}
 
pascal ComponentResult SGPictSetChannelMatrix (SGPictGlobals store, const MatrixRecord *m)
{
    OSErr err = noErr;
    MatrixRecord mat;
    short matType;
 
    // determine the matrix being set
    if (m)
        mat = *m;
    else
        SetIdentityMatrix(&mat);
 
    // validate it
    matType = GetMatrixType(&mat);
    if ((mat.matrix[0][0] < 0) || (mat.matrix[1][1] < 0) || (matType >= linearMatrixType))
        return paramErr;
 
    // update the matrix and destination rect
    store->displayMatrix = mat;
    store->destRect = store->srcRect;
    TransformRect(&mat, &store->destRect, nil);
 
    return err;
}
 
pascal ComponentResult SGPictGetChannelMatrix (SGPictGlobals store, MatrixRecord *m)
{
    *m = store->displayMatrix;
 
    return noErr;
}
 
pascal ComponentResult SGPictSetChannelBounds (SGPictGlobals store, const Rect *bounds)
{
    // remember destination rect
    store->destRect = *bounds;
    // recalculate display matrix from it
    RectMatrix(&store->displayMatrix, &store->srcRect, &store->destRect);
 
    return noErr;
}
 
pascal ComponentResult SGPictGetChannelBounds (SGPictGlobals store, Rect *bounds)
{
    *bounds = store->destRect;
 
    return noErr;
}
 
pascal ComponentResult SGPictSetChannelClip (SGPictGlobals store, RgnHandle theClip)
{
    OSErr err = noErr;
 
    if (store->clip) {
        DisposeRgn(store->clip);
        store->clip = nil;
    }
    if (theClip) {
        err = HandToHand((Handle *)&theClip);
        store->clip = theClip;
    }
 
    return err;
}
 
pascal ComponentResult SGPictGetChannelClip (SGPictGlobals store, RgnHandle *theClip)
{
    OSErr err = noErr;
 
    if (*theClip = store->clip)
        err = HandToHand((Handle *)theClip);
 
    return err;
}
 
pascal ComponentResult SGPictStartPreview (SGPictGlobals store)
{
    store->inPreview = (store->usage & seqGrabPreview) != 0;
    return noErr;
}
 
pascal ComponentResult SGPictStartRecord (SGPictGlobals store)
{
    store->inRecord = (store->usage & seqGrabRecord) != 0;
    store->inPreview = (store->usage & seqGrabPlayDuringRecord) != 0;
    return noErr;
}
 
pascal ComponentResult SGPictIdle (SGPictGlobals store)
{
    OSErr err = noErr;
 
    if (!store->paused && (store->inRecord || store->inPreview)) {
        Point mouseLoc;
        Rect r;
        PicHandle tempPict = nil;
        TimeRecord tr;
        CGrafPtr savePort;
        GDHandle saveGD;
        Rect maxR;
    
        GetGWorld(&savePort, &saveGD);
 
        GetTimeBaseTime(store->base, kMediaTimeScale, &tr); 
        
        // figure the current area around the mouse (only on main screen)
        SetGWorld(store->tempPort, GetMainDevice());
        GetMouse(&mouseLoc);
        LocalToGlobal(&mouseLoc);
        r.top = r.bottom = mouseLoc.v;
        r.left = r.right = mouseLoc.h;
        InsetRect(&r, -(store->srcRect.right >> 1), -(store->srcRect.bottom >> 1));
        maxR = (**GetMainDevice()).gdRect;
        if (r.left < maxR.left) OffsetRect(&r, -r.left + maxR.left, 0);
        if (r.top < maxR.top) OffsetRect(&r, 0, -r.top + maxR.top);
        if (r.right > maxR.right) OffsetRect(&r, maxR.right - r.right, 0);
        if (r.bottom > maxR.bottom) OffsetRect(&r, 0, maxR.bottom - r.bottom);
 
        // copy the screen into a picture
        tempPict = OpenPicture(&r);
            CopyBits((BitMap *)&store->tempPort->portPixMap, 
                        (BitMap *)&store->tempPort->portPixMap, &r, &r, srcCopy, nil);
            if (store->showTickCount) {
                // if they want to see ticks, draw them
                Str63 str;
 
                NumToString(TickCount(), str);
                r.right = r.left + StringWidth(str) + 4; // do some magic positioning
                r.bottom = r.top + 14;
                EraseRect(&r);
                MoveTo(r.left + 2, r.bottom - 3);
                TextSize(12);
                DrawString(str);
            }
 
        ClosePicture();
 
        // if recording, add data to movie
        if (store->inRecord) {
            long offset;
            long pictSize = GetHandleSize((Handle)tempPict);
 
            HLock((Handle)tempPict);
            err = SGAddMovieData(store->grabber, store->self, 
                        (Ptr)*tempPict, pictSize, &offset, 0, tr.value.lo, seqGrabWriteAppend);
            store->bytesWritten += pictSize;
        }
 
        // if we need to show the preview image, do that
        if (store->inPreview) {
            RgnHandle saveClip;
 
            SetGWorld(store->destPort, store->destGD);
            if (store->clip) {
                saveClip = NewRgn();
                GetClip(saveClip);
                SetClip(store->clip);
            }
            DrawPicture(tempPict, &store->destRect);
            if (store->clip) {
                SetClip(saveClip);
                DisposeRgn(saveClip);
            }
        }
 
        KillPicture(tempPict);
 
        SetGWorld(savePort, saveGD);
    }
 
    return err;
}
 
pascal ComponentResult SGPictStop (SGPictGlobals store)
{
    store->inRecord = store->inPreview = false;
 
    return noErr;
}
 
pascal ComponentResult SGPictPause (SGPictGlobals store, Byte pause)
{
    store->paused = pause;
 
    return noErr;
}
 
pascal ComponentResult SGPictPrepare (SGPictGlobals store, Boolean prepareForPreview, Boolean prepareForRecord)
{
    store->bytesWritten = 0;
 
    return noErr;
}
 
pascal ComponentResult SGPictRelease (SGPictGlobals store)
{
    return noErr;
}
 
pascal ComponentResult SGPictGetChannelSampleDescription (SGPictGlobals store, Handle sampleDesc)
{
    OSErr err;
    SampleDescriptionPtr sdp;
 
    SetHandleSize(sampleDesc, sizeof(SampleDescription));
    if (err = MemError()) goto bail;
 
    // make up a minimal sample description
    sdp = (SampleDescriptionPtr)*sampleDesc;
    sdp->descSize = sizeof(SampleDescription);
    sdp->dataFormat = 'PICT';
    sdp->resvd1 = 0;
    sdp->resvd2 = 0;
    sdp->dataRefIndex = 0;
 
bail:
    return err;
}
 
pascal ComponentResult SGPictGetChannelDeviceList (SGPictGlobals store, long selectionFlags, SGDeviceList *list)
{
    *list = (SGDeviceList)NewHandleClear(sizeof(SGDeviceListRecord));       // no devices
 
    return MemError();
}
 
pascal ComponentResult SGPictSetChannelDevice (SGPictGlobals store, StringPtr name)
{
    return noErr;
}
 
pascal ComponentResult SGPictGetChannelTimeScale (SGPictGlobals store, TimeScale *scale)
{
    *scale = kMediaTimeScale;
 
    return noErr;
}
 
pascal ComponentResult SGPictInitChannel (SGPictGlobals store, SeqGrabComponent owner)
{
    // initialize any variable here
    SetRect(&store->srcRect, 0, 0, 160, 120);    // size around mouse that we capture
    SetIdentityMatrix(&store->displayMatrix);
 
    store->grabber = owner;
    SGGetTimeBase(owner, &store->base);
 
    return noErr;
}
 
pascal ComponentResult SGPictWriteSamples (SGPictGlobals store, Movie m, AliasHandle theFile)
{
    OSErr err = 0;
    Track pictT;
    Media pictM;
    long i;
    MatrixRecord aMatrix;
    Rect from, to;
    seqGrabFrameInfo fi;
    TimeRecord tr;
    TimeValue mediaDuration;
    SampleDescriptionHandle sampleDesc = 0;
 
    if (!(store->usage & seqGrabRecord))
        return err;
 
    sampleDesc = (SampleDescriptionHandle)NewHandle(4);
    if (err = MemError()) goto bail;
    if (err = SGGetChannelSampleDescription(store->self, (Handle)sampleDesc)) goto bail;
 
    SetRect(&from, 0, 0, store->srcRect.right, store->srcRect.bottom);
    to = from;
    TransformRect(&store->displayMatrix, &to, nil);
 
    pictT = NewMovieTrack(m, (long)from.right << 16, (long)from.bottom << 16, 0);
    pictM = NewTrackMedia(pictT, 'PICT', kMediaTimeScale, (Handle)theFile, rAliasType);
 
    fi.frameChannel = store->self;
    i = -1;
    do {
        TimeValue frameDuration;
 
        err = SGGetNextFrameReference(store->grabber, &fi, &frameDuration, &i);
        if (err) {
            if (err == paramErr)
                err = 0;
            break;
        }
 
        err = AddMediaSampleReference(pictM, 
                fi.frameOffset, fi.frameSize,
                frameDuration,
                sampleDesc, 1,
                0, 0);
 
        if (err == invalidDuration) {
            err = noErr;
            break;
        }
    } while (!err);
 
done:
    if (err) goto bail;
 
    GetTimeBaseTime(store->base, 0, &tr);
    ConvertTimeScale(&tr, kMediaTimeScale);     // trim media inserted to not extend beyond end time
    mediaDuration = GetMediaDuration(pictM);
    if (tr.value.lo > mediaDuration)
        tr.value.lo = mediaDuration;        // this should never happen, 
                                    // but avoiding an error at this step seems like a good idea
    err = InsertMediaIntoTrack(pictT, 0, 0, tr.value.lo, kFix1);
 
    RectMatrix(&aMatrix, &from, &to);
    SetTrackMatrix( pictT, &aMatrix );
    if (store->clip) SetTrackClipRgn(pictT, store->clip);
 
bail:
 
    DisposHandle((Handle)sampleDesc);
    return err;
}
 
pascal ComponentResult SGPictGetDataRate (SGPictGlobals store, long *bytesPerSecond)
{
    // take a guess at our data rate
    *bytesPerSecond = 24 * 1024;
    if (store->bytesWritten) {
        TimeValue timeNow = GetTimeBaseTime(store->base, 8, nil); // 1/8th second resolution
 
        if (!timeNow)
            return seqGrabInfoNotAvailable;
 
        *bytesPerSecond = (store->bytesWritten / timeNow) * 8;  // convert back to seconds
    }
 
    return noErr;
}
 
/************************
    SG Pict Channel
    Specific Calls
************************/
 
pascal ComponentResult SGPictSetShowTickCount (SGPictGlobals store, Boolean show)
{
    store->showTickCount = show;
    return noErr;
}
 
pascal ComponentResult SGPictGetShowTickCount (SGPictGlobals store, Boolean *show)
{
    *show = store->showTickCount;
    return noErr;
}
 
/************************
    Panel Calls
************************/
 
pascal ComponentResult SGPictPanelGetDitl (SGPictGlobals store, Handle *ditl)
{
    *ditl = GetResource('DITL', 7000); 
    if (!*ditl) return resNotFound;
    DetachResource(*ditl);
    return noErr;
}
 
pascal ComponentResult SGPictPanelInstall (SGPictGlobals store, SGChannel c, DialogPtr d, short itemOffset)
{
    Rect newBounds;
    short kind;
    Handle h;
 
    // reset this channel to use the dialog window and be in preview mode with no clip
    SGSetGWorld(store->self, (CGrafPtr)d, GetMainDevice());
    SGGetChannelUsage(store->self, &store->saveUsage);
    SGSetChannelUsage(store->self, seqGrabPreview);
 
    SGSetChannelClip(c, nil);
 
    GetDItem(d, 1 + itemOffset, &kind, &h, &newBounds);
    SGSetChannelBounds(c, &newBounds);
 
    SGStartPreview(store->self);
 
    return noErr;
}
 
pascal ComponentResult SGPictPanelEvent (SGPictGlobals store, SGChannel c, DialogPtr d, short itemOffset, 
            EventRecord *theEvent, short *itemHit, Boolean *handled)
{
    if (theEvent->what == nullEvent)
        return SGIdle(store->self);
 
    return noErr;
}
 
pascal ComponentResult SGPictPanelRemove (SGPictGlobals store, SGChannel c, DialogPtr d, short itemOffset)
{
    SGStop(store->self);
    SGRelease(store->self);
 
    // note that the clip and bounds are automatically restored for us because they were stored by us with the
    //  SGGetSettings call
 
    SGSetChannelUsage(store->self, store->saveUsage);
 
    return noErr;
}
 
pascal ComponentResult SGPictPanelGetSettings (SGPictGlobals store, SGChannel c, UserData *result, long flags)
{
    OSErr err = noErr;
    UserData ud = 0;
    MatrixRecord matrix;
    RgnHandle clip;
 
    if (err = NewUserData(&ud)) goto bail;
 
    // add matrix to user data
    if (SGGetChannelMatrix(c, &matrix) == noErr) {
        if (err = SetUserDataItem(ud, &matrix, sizeof(matrix), sgMatrixType, 1)) goto bail;
    }
 
    // store clip, if there is one
    if (SGGetChannelClip(c, &clip) == noErr) {
        if (clip)
            err = AddUserData(ud, (Handle)clip, sgClipType);
        else
            err = SetUserDataItem(ud, nil, 0, sgClipType, 1);   // add a dummy to indicate none
        DisposeRgn(clip);
        if (err) goto bail;
    }
 
bail:
    if (err) {
        DisposeUserData(ud);
        ud = 0;
    }
    *result = ud;
 
    return err;
}
 
pascal ComponentResult SGPictPanelSetSettings (SGPictGlobals store, SGChannel c, UserData ud, long flags)
{
    OSErr err;
    RgnHandle clip = NewRgn();
    MatrixRecord matrix;
 
    // restore clip, if one was stored
    if (GetUserData(ud, (Handle)clip, sgClipType, 1) == noErr) {
        if (err = SGSetChannelClip(c, GetHandleSize((Handle)clip) ? clip : 0)) goto bail;
    }
 
    // restore matrix
    if (err = GetUserDataItem(ud, &matrix, sizeof(matrix), sgMatrixType, 1)) goto bail;
    if (err = SGSetChannelMatrix(c, &matrix)) goto bail;
 
bail:
    DisposeRgn(clip);
    return err;
}