source/fileIO.c

/****************************************************/
/*                                                  */
/*  File:       fileIO.c                            */
/*                                                  */
/*  Program:    Imageer                             */
/*                                                  */
/*  By:         Jason Hodges-Harris                 */
/*                                                  */
/*  Created:    26/10/95  00:00:00 AM               */
/*                                                  */
/*  Version:    1.0.0d3                             */
/*                                                  */
/*  Copyright:  © 1995-96 Apple Computer, Inc.,     */ 
/*                  all rights reserved.            */      
/*                                                  */
/****************************************************/
 
 
/**** Macintosh Toolbox Headers *****/
 
#ifndef __COMPONENTS__
#include <Components.h>
#endif
 
#ifndef __CONTROLS__
#include <Controls.h>
#endif
 
#ifndef __CURSORCTL__
#include <CursorCtl.h>
#endif
 
#ifndef __DIALOGS__
#include <Dialogs.h>
#endif
 
#ifndef __FILES__
#include <Files.h>
#endif
 
#ifndef __GXGRAPHICS__
#include <GXGraphics.h>
#endif
 
#ifndef __GXMATH__
#include <GXMath.h>
#endif
 
#ifndef __IMAGECOMPRESSION__
#include <ImageCompression.h>
#endif
 
#ifndef __MEMORY__
#include <Memory.h>
#endif
 
#ifndef __PICTUTILS__
#include <PictUtils.h>
#endif
 
#ifndef __QUICKTIMECOMPONENTS__
#include <QuickTimeComponents.h>
#endif
 
#ifndef __STANDARDFILE__
#include <StandardFile.h>
#endif
 
#ifndef __STRING__
#include <string.h>
#endif
 
#ifndef __STRINGS__
#include <Strings.h>
#endif
 
#ifndef __TOOLUTILS__
#include <ToolUtils.h>
#endif
 
#ifndef __TYPES__
#include <Types.h>
#endif
 
 
/****   Application headers and prototypes   ****/
 
 
#ifndef __IMAGEERAPPHEADER__
#include "Imageer.app.h"
#endif
 
#ifndef __IMAGEERPROTOSHEADER__
#include "Imageer.protos.h"
#endif
 
 
/****   Global variables    ****/
 
extern OSType   gLoadFileType;
static OSType   gSaveFileType;
 
 
/****   Default RGB Colors  ****/
 
static const RGBColor   kRGBBlack = {0x0000, 0x0000, 0x0000};
static const RGBColor   kRGBWhite = {0xFFFF, 0xFFFF, 0xFFFF};
 
 
/****   Load supported image file type  ****/
 
// Main function which is called when 'Open' is selected in the file menu.
// Displays the custom get file dialog, which supports a pop up menu
// to filter out unwanted/unwanted file types. Creates and initialises the
// image window document structure dependent of the file type loaded.
 
#pragma segment fileSys
OSErr   LoadSupportedImage(void)
{
    ImageDocHndl        theDocHndl = nil;
    PicHandle           thePictHndl = nil;
    DlgHookYDUPP        theDialogHookUPP;
    FileFilterYDUPP     theDialogFileFilterUPP;
    PictInfo            thePictInfo;
    StandardFileReply   theSFReply;
    TiffInfo            theTiffStruct;
    SFTypeList          fileTypes;
    Point               dlogPosition = {-1,-1};
    OSErr               error = noErr;
    
    // Create Universal Proc Ptrs for custom Dialog functions
    theDialogHookUPP = NewDlgHookYDProc(CustomGetFileHook);
    theDialogFileFilterUPP = NewFileFilterYDProc(CustomFileDlogFilter);
    CustomGetFile (theDialogFileFilterUPP,-1,
                   fileTypes,&theSFReply,rCustomOpenDialog,
                   dlogPosition,theDialogHookUPP,nil,nil,nil,nil);
    DisposeRoutineDescriptor((UniversalProcPtr)theDialogHookUPP);
    DisposeRoutineDescriptor((UniversalProcPtr)theDialogFileFilterUPP);
    if (theSFReply.sfGood == true)
    {
        theDocHndl = (ImageDocHndl)NewHandle(sizeof(ImageDoc)); // create new image document
        if (!theDocHndl)
        {
            DisplayAlert (rGenWarning,rErrMessages,iFailAllocMem);
            return kFailAllocDocHndl;
        }
        if (theSFReply.sfType == 'qdgx')
            (**theDocHndl).isUsingQDGX = true;
        else
            (**theDocHndl).isUsingQDGX = false;         
        (**theDocHndl).theImageFileReply = theSFReply;  // copy SFReply to window Doc structure
        switch (theSFReply.sfType)
        {
            case ('TIFF'):                              // TIFF file type
                // Set up document structure to defaults for image type
                (**theDocHndl).theFileType = TiffType;
                (**theDocHndl).theImageChanged = false;
                (**theDocHndl).theColorsPalette = nil;
                (**theDocHndl).theImageWorld = nil;
                (**theDocHndl).theVScrollBar = nil;
                (**theDocHndl).theHScrollBar = nil;
                (**theDocHndl).theUndoState = kCannotUndo;
                (**theDocHndl).hasUndoTemp = false;
                (**theDocHndl).hasRedoTemp = false;
                theTiffStruct.tiffCTabHndl = nil;       // set to nil as Color table not always created
                GetTIFFHdrInfo(theDocHndl,&theTiffStruct);
                error = GetTIFFIFDirectory(theDocHndl,&theTiffStruct);
                if (error == kTIFFNotSupported)
                {
                    if (theTiffStruct.tiffCTabHndl)
                        DisposeHandle((Handle)theTiffStruct.tiffCTabHndl);
                    CleanLoadAbort(theDocHndl);
                    DisplayAlert (rGenWarning, rFileIOMessages, iTiffNotSupported);
                    return error;
                }
                // cannot at present support TIFF files that aren't 8 or 32 bit
                switch (theTiffStruct.bitDepth)
                {
                    case 8:
                        (**theDocHndl).theImageWorld = 
                            CreateOffscreen(theTiffStruct.tiffCTabHndl,
                                            theTiffStruct.xImageSize,
                                            theTiffStruct.yImageSize,
                                            theTiffStruct.bitDepth,kNoFlags);
                        if ((**theDocHndl).theImageWorld == nil)
                        {
                            DisposeHandle((Handle)theDocHndl);
                            return kFailMakeGWorld;
                        }
                    break;
                    case 32:
                        (**theDocHndl).theImageWorld = 
                            CreateOffscreen(nil,
                                            theTiffStruct.xImageSize,
                                            theTiffStruct.yImageSize,
                                            theTiffStruct.bitDepth,kNoFlags);
                        if ((**theDocHndl).theImageWorld == nil)
                        {
                            DisposeHandle((Handle)theDocHndl);
                            return kFailMakeGWorld;
                        }
                    break;
                    default:
                        DisplayAlert (rGenWarning,rErrMessages,iNoSupportBitDepth);
                        return kBitDepthErr;
                    break;
                }
                // Fill in image coord sizes and bit depth to document structure
                (**theDocHndl).theImageXSize = theTiffStruct.xImageSize;
                (**theDocHndl).theImageYSize = theTiffStruct.yImageSize;
                (**theDocHndl).theImageDepth = theTiffStruct.bitDepth;
                // Perform TIFF file parse directly into GWorld referenced from document structure
                error = LoadTiffToGWorld (theDocHndl,&theTiffStruct,theTiffStruct.bitDepth);
                if (error)
                {
                    DisplayAlert (rGenAlert,0,0);
                    return error;
                }
                SetUndoItemText(kCannotUndo);
                error = CreateImageWindow (theDocHndl);
                if (error == kDefaultAppError)
                {
                    DisplayAlert (rGenAlert,0,0);
                    return error;
                }
                // Dispose of Tiff struct color table handle 
                if (theTiffStruct.tiffCTabHndl)
                    DisposeHandle((Handle)theTiffStruct.tiffCTabHndl);
                error = AddDocNameToMenu(theDocHndl);
                if (error)
                    DisplayAlert (rGenAlert,0,0);
                else
                    return noErr;
            break;
            case ('PICT'):                              // PICT type
                // Set up document structure to defaults for image type
                (**theDocHndl).theFileType = PictType;
                (**theDocHndl).theImageChanged = false;
                (**theDocHndl).theColorsPalette = nil;
                (**theDocHndl).theImageWorld = nil;
                (**theDocHndl).theVScrollBar = nil;
                (**theDocHndl).theHScrollBar = nil;
                (**theDocHndl).theUndoState = kCannotUndo;
                (**theDocHndl).hasUndoTemp = false;
                (**theDocHndl).hasRedoTemp = false;
                // Load PICT file into PicHandle structure before placing in GWorld
                thePictHndl = LoadPictImageFile(theDocHndl);
                if (!thePictHndl)
                {
                    DisposeHandle((Handle)theDocHndl);
                    return kDefaultAppError;
                }
                // test if minimal temp memory free available. If none available GetPictInfo() fails.
                if (TempFreeMem() < kMinLowMem)
                {
                    DisplayAlert(rGenWarning,rErrMessages,iLowTempMem);
                    DisposeHandle((Handle)theDocHndl);
                    return kLowMemWarning;
                }
                error = GetPictInfo (thePictHndl, &thePictInfo, returnColorTable,256,systemMethod,0);
                // Fill in image coord sizes and bit depth to document structure
                (**theDocHndl).theImageXSize = thePictInfo.sourceRect.right - thePictInfo.sourceRect.left;
                (**theDocHndl).theImageYSize = thePictInfo.sourceRect.bottom - thePictInfo.sourceRect.top;
                (**theDocHndl).theImageDepth = thePictInfo.depth;
                // Create window document's GWorld structure and draw loaded Pict image into it.
                if (!error && thePictInfo.depth <= 8)
                    (**theDocHndl).theImageWorld = 
                        CreateOffscreen(thePictInfo.theColorTable,
                                        (**theDocHndl).theImageXSize,
                                        (**theDocHndl).theImageYSize,
                                        (**theDocHndl).theImageDepth,kNoFlags);
                else
                    (**theDocHndl).theImageWorld = 
                        CreateOffscreen(nil,
                                        (**theDocHndl).theImageXSize,
                                        (**theDocHndl).theImageYSize,
                                        (**theDocHndl).theImageDepth,kNoFlags);
                
                if ((**theDocHndl).theImageWorld == nil)
                {
                    DisposeHandle((Handle)theDocHndl);      // dispose image document
                    DisposeHandle((Handle)thePictHndl);     // dispose picHandle
                    return kFailMakeGWorld;
                }
                error = DrawPictToGWorld (theDocHndl,thePictHndl);
                SetUndoItemText(kCannotUndo);
                error = CreateImageWindow (theDocHndl);
                if (error == kDefaultAppError)
                {
                    DisplayAlert (rGenAlert,0,0);
                    return error;
                }
                error = AddDocNameToMenu(theDocHndl);
                if (error)
                    DisplayAlert (rGenAlert,0,0);
                else
                    return noErr;
            break;
            case ('qdgx'):
                // QuickDraw GX file load function is not yet supported in this release. 
                // It will be included in a future release.
                SetUndoItemText(kCannotUndo);
                DisplayAlert (rGenAlert,rErrMessages,iNotImplemented);
                return kNotSupported;
            break;
        }
    }
    return kFileLoadAbortErr;
}
 
 
/****   CustomGetFile   dialog file hook func   ****/
 
// This function handles user interaction of the custom dialog items added to the
// CustomGetFile dialog and ignores all other items which are returned and processed
// by the standard internal function.
 
#pragma segment fileSys
pascal short    CustomGetFileHook(short item,DialogPtr theDlogPtr,void* theData)
{
    Handle              theHandle;
    Rect                theRect;
    short               theType,
                        ignored;
    
    switch (item)   
    {
        case sfHookFirstCall:
            GetDItem(theDlogPtr,kLoadFilePopUpItem,&theType,&theHandle,&theRect);
            switch (gLoadFileType)
            {
                case 'TIFF':
                    theType = iTiffType;
                break;
                case 'PICT':
                    theType = iPictType;
                break;
                case 'qdgx':
                    theType = iGXType;
                break;
                default:
                    theType = iAllFiles;
                break;
            }
            SetCtlValue((ControlHandle)theHandle,theType);
            return sfHookNullEvent;
        break;
        case kLoadFilePopUpItem:
            GetDItem(theDlogPtr,item,&ignored,&theHandle,&theRect);
            theType = GetCtlValue((ControlHandle)theHandle);
            switch (theType)
            {
                case iTiffType:
                    gLoadFileType = 'TIFF';
                    item = sfHookRebuildList;
                break;
                case iPictType:
                    gLoadFileType = 'PICT';
                    item = sfHookRebuildList;
                break;
                case iGXType:
                    gLoadFileType = 'qdgx';
                    item = sfHookRebuildList;
                break;
                case iAllFiles:
                    gLoadFileType = '????';
                    item = sfHookRebuildList;
                break;
            }
            return item;
        break;
    }
    return item;
}   
 
 
/****   basic CustomGetFile filter function ****/
 
// File filter function is used by CustomGetFile() to restrict the
// display of files and folders to supported image file types and visible folders.
 
#pragma segment fileSys
pascal Boolean  CustomFileDlogFilter(CInfoPBPtr theParamBlok, Ptr theDataPtr)
{
    Boolean     notDisplayed = true;
    
    switch (theParamBlok->hFileInfo.ioFlFndrInfo.fdType)
    {
        case 'TIFF':
            if (gLoadFileType == 'TIFF' || gLoadFileType == '????')
                notDisplayed = false;
        break;
        case 'PICT':
            if (gLoadFileType == 'PICT' || gLoadFileType == '????')
                notDisplayed = false;
        break;
        case 'qdgx':
            if (gLoadFileType == 'qdgx' || gLoadFileType == '????')
                notDisplayed = false;
        break;
        default:
            if ((theParamBlok->dirInfo.ioFlAttrib & kFolderBit) && 
                !(theParamBlok->hFileInfo.ioFlFndrInfo.fdFlags & kFileVisibleBit))
                    notDisplayed = false;
        break;
    }
    return  notDisplayed;
}
 
 
/****   retrieve TIFF image header information  ****/
 
// Retrieve TIFF file header information and parse. Used to calculate file information
// when reading in TIFF directory data.
 
#pragma segment fileSys
void    GetTIFFHdrInfo(ImageDocHndl theDocHndl, TiffInfo *theTiffHeader)
{
    long                fileLen;
    StandardFileReply   theFileSpec = (**theDocHndl).theImageFileReply; // use local as FSpOpenDF returns param in spec
    char                headerStore[8];
    
    FSpOpenDF(&theFileSpec.sfFile,fsRdPerm,&theFileSpec.sfFile.vRefNum); // open file
    // Get size of file storage of TIFF header and read in TIFF header info
    fileLen = sizeof(headerStore);                  
    FSRead(theFileSpec.sfFile.vRefNum,&fileLen,headerStore);    
    FSClose(theFileSpec.sfFile.vRefNum);
    // copy header information to data structure
    BlockMoveData(headerStore,&theTiffHeader->byteOrder,2);
    BlockMoveData(&headerStore[2],&theTiffHeader->version,2);
    if (theTiffHeader->byteOrder == IntlFormat)
        theTiffHeader->version = SwapByteOrder(theTiffHeader->version,2);
    BlockMoveData(&headerStore[4],&theTiffHeader->offsetToIFD,4);
    if (theTiffHeader->byteOrder == IntlFormat)
        theTiffHeader->offsetToIFD = SwapByteOrder(theTiffHeader->offsetToIFD,4);
    return;
}
 
 
/****   Retrieve TIFF format image file directory info  ****/
 
// Function to parse the TIFF image file and load the image
// data directly into a previously created GWorld PixMap.
// If the TIFF file is an indexed image the color table information
// is used to manually create a Color Table
 
#pragma segment fileSys
OSErr   GetTIFFIFDirectory(ImageDocHndl theDocHndl, TiffInfo *theTiffHeader)
{
    long                fileLen,
                        tempDecode,         // temporary decoder storage
                        myOldFilePos,
                        cTabSize;
    StandardFileReply   theFileSpec = (**theDocHndl).theImageFileReply; // use local as FSpOpen returns param in spec
    char                iFDcounter[2],      // number of IFD entries
                        iFDStore[12],       // IFD field entry
                        iFDOffset[4];       // offset to next IFD
    short               entriesLeft,        // IFD's to process
                        numberColors,
                        colorValue,
                        countStep,
                        count;
    OSErr               error = noErr,
                        myFileErr;
    Boolean             isIntFormat = false;
    
    if (theTiffHeader->byteOrder == IntlFormat)
        isIntFormat = true;
    FSpOpenDF(&theFileSpec.sfFile,fsRdPerm,&theFileSpec.sfFile.vRefNum);
    SetFPos(theFileSpec.sfFile.vRefNum,fsFromStart,theTiffHeader->offsetToIFD);
    fileLen = sizeof(iFDcounter);
    FSRead(theFileSpec.sfFile.vRefNum,&fileLen,iFDcounter);
    BlockMoveData(iFDcounter,&entriesLeft,2);
    if (isIntFormat)
        entriesLeft = SwapByteOrder(entriesLeft,2);
    fileLen = sizeof(iFDStore);
    do{
        do{
            myFileErr = FSRead(theFileSpec.sfFile.vRefNum,&fileLen,iFDStore);   // get pattern header info
            if (myFileErr)
                DebugStr("\pError in reading IFDirectory");
            BlockMoveData(iFDStore,&tempDecode,2);
            tempDecode=tempDecode>>16;
            if (isIntFormat)
                tempDecode = SwapByteOrder(tempDecode,2);
            switch (tempDecode)
            {
                case 0xFE:          // get image subfile type
                    BlockMoveData(&iFDStore[8],&tempDecode,4);
                    if (tempDecode!=0)
                        DebugStr("\pIFD new subfile type not supported"); 
                break;
                case 0x100:         // image width
                    BlockMoveData(&iFDStore[2],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    if (tempDecode==3)
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,2);
                        tempDecode=tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                    }
                    else    
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,4);
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,4);
                    }
                    theTiffHeader->xImageSize = tempDecode;
                break;
                case 0x101:         // image length
                    BlockMoveData(&iFDStore[2],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    if (tempDecode==3)
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,2);
                        tempDecode=tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                    }
                    else    
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,4);
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,4);
                    }
                    theTiffHeader->yImageSize = tempDecode;
                break;
                case 0x102:         // bits per sample
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    theTiffHeader->bitDepth = tempDecode;
                break;
                case 0x103:         // image compression NOT SUPPORTED YET!!
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    theTiffHeader->compressionType = tempDecode;
                    if (tempDecode != 1)
                        return kTIFFNotSupported;
                break;
                case 0x106:         // Photometricinterpretation
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    theTiffHeader->PhotoInterpret = tempDecode;
                    if (tempDecode > 3)
                        return kTIFFNotSupported;
                break;
                case 0x111:         // strip offsets for one strip. Is offset to data
                    BlockMoveData(&iFDStore[2],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (tempDecode==3)
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,2);
                        tempDecode=tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                    }
                    else    
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,4);
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,4);
                    }
                    theTiffHeader->imageOffset = tempDecode;    // offset to image strip
                break;
                case 0x115:         // samples per pixel 1 for palette, 3 for direct
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    theTiffHeader->bitDepth = tempDecode*8;
                    if (theTiffHeader->bitDepth == 24)
                        theTiffHeader->bitDepth = 32;   // amend to 32 bpp to correctly create GWorld 
                    if (theTiffHeader->PhotoInterpret < 2)
                    {
                        if (theTiffHeader->bitDepth == 8)   // 8 Bit Greyscale image
                        {
                            numberColors = 256;         // number of entries in palette
                            countStep = 1;
                        }
                        if (theTiffHeader->bitDepth == 4)   // 4 Bit Greyscale image
                        {
                            numberColors = 16;
                            countStep = 16;
                        }
                        // Manually create the Color Table from TIFF file information
                        cTabSize = sizeof(ColorTable)+(sizeof(ColorSpec)*(numberColors-1));
                        theTiffHeader->tiffCTabHndl = (CTabHandle) NewHandle(cTabSize);
                        if (theTiffHeader->tiffCTabHndl == nil || MemError())
                        {
                            return kDefaultAppError;
                        }
                        (**theTiffHeader->tiffCTabHndl).ctFlags = 0;
                        (**theTiffHeader->tiffCTabHndl).ctSize = numberColors-1;
                        switch (theTiffHeader->PhotoInterpret)
                        {
                            case 0:
                                for (count=0;count<numberColors;count+=countStep)
                                {
                                    colorValue = (255 - count) << 8;
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].value = count;
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.red = colorValue;        // red component
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.green = colorValue;      // green component
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.blue = colorValue;       // blue component
                                }
                            break;
                            
                            case 1:
                                for (count=0;count<numberColors;count+=countStep)
                                {
                                    
                                    colorValue = count << 8;
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].value = count;
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.red = colorValue;    // red component
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.green = colorValue;  // green component
                                    (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.blue = colorValue;   // blue component
                                }
                            break;
                        }
                        CTabChanged(theTiffHeader->tiffCTabHndl);
                    }
                break;
                case 0x116:         // number of rows per image strip
                    BlockMoveData(&iFDStore[2],&tempDecode,2);
                    tempDecode = tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    if (tempDecode == 3)
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,2);
                        tempDecode=tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                    }
                    else
                    {
                        BlockMoveData(&iFDStore[8],&tempDecode,4);
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,4);
                    }
                    theTiffHeader->rowStrip = tempDecode;
                break;
                case 0x11C:
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    theTiffHeader->PlanarConfig = tempDecode>>16;
                break;
                case 0x140:         // image clut for palette images
                    if (theTiffHeader->bitDepth == 8)   // 8 Bit palette image
                        numberColors = 256;     // number of entries in palette
                    if (theTiffHeader->bitDepth == 4)   // 4 Bit palette image
                        numberColors = 16;
                    cTabSize = sizeof(ColorTable)+(sizeof(ColorSpec)*(numberColors-1));
                    theTiffHeader->tiffCTabHndl = (CTabHandle) NewHandle(cTabSize);
                    if (theTiffHeader->tiffCTabHndl==nil)
                    {
                        return kDefaultAppError;
                    }
                    (**theTiffHeader->tiffCTabHndl).ctFlags = 0;
                    (**theTiffHeader->tiffCTabHndl).ctSize = numberColors-1;
                    BlockMoveData(&iFDStore[8],&tempDecode,4);
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,4);
                    GetFPos(theFileSpec.sfFile.vRefNum,&myOldFilePos);  // get old file position
                    SetFPos(theFileSpec.sfFile.vRefNum,fsFromStart,tempDecode); // set new file pos
                    fileLen = sizeof(short);
                    for (count=0;count<numberColors;count++)
                    {
                        (**theTiffHeader->tiffCTabHndl).ctTable[count].value = count;
                        FSRead(theFileSpec.sfFile.vRefNum,&fileLen,&tempDecode);
                        tempDecode = tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                        (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.red = tempDecode;        // red component
                    }
                    for (count=0;count<numberColors;count++)
                    {
                        FSRead(theFileSpec.sfFile.vRefNum,&fileLen,&tempDecode);
                        tempDecode = tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                        (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.green = tempDecode;      // green component
                    }
                    for (count=0;count<numberColors;count++)
                    {
                        FSRead(theFileSpec.sfFile.vRefNum,&fileLen,&tempDecode);
                        tempDecode = tempDecode>>16;
                        if (isIntFormat)
                            tempDecode = SwapByteOrder(tempDecode,2);
                        (**theTiffHeader->tiffCTabHndl).ctTable[count].rgb.blue = tempDecode;       // blue component
                    }
                    CTabChanged(theTiffHeader->tiffCTabHndl);
                    SetFPos(theFileSpec.sfFile.vRefNum,fsFromStart,myOldFilePos);
                break;
                case 0x153: // Sample Format tag
                    BlockMoveData(&iFDStore[8],&tempDecode,2);
                    tempDecode=tempDecode>>16;
                    if (isIntFormat)
                        tempDecode = SwapByteOrder(tempDecode,2);
                    if (tempDecode != 1)
                        return kTIFFNotSupported;
                break;
                case 0x142: // handle unsupported tags which could cause incorrect image display
                case 0x143:
                case 0x152:
                    return kTIFFNotSupported;
                break;
            }   
            entriesLeft--;
        } while (entriesLeft!=0);               // read all tags
        // multi IFD stuff Needs to be expanded to support 
        fileLen = sizeof(iFDOffset);
        FSRead(theFileSpec.sfFile.vRefNum,&fileLen,iFDOffset);// get pattern header info
        BlockMoveData(iFDOffset,&theTiffHeader->offsetToIFD,4);
        if (isIntFormat)
            tempDecode = SwapByteOrder(tempDecode,4);
        SetFPos(theFileSpec.sfFile.vRefNum,fsFromStart,theTiffHeader->offsetToIFD);
        theTiffHeader->offsetToIFD = 0;         // force to zero as don't support multiple images in file
    } while (theTiffHeader->offsetToIFD!=0);    // read next IFD if present
    FSClose(theFileSpec.sfFile.vRefNum);        // close open file
    return  error;
}
 
 
/****       Draw the TIFF image data directly into the offscreen pixmap ****/
 
#pragma segment fileSys
OSErr LoadTiffToGWorld (ImageDocHndl theImageDocHndl, TiffInfoPtr theTiffInfo, short bitDepth)
{
    PixMapHandle        thePixMapHndl = nil;
    CursHandle          watchCrsr;
    Ptr                 imageLinePtr = nil,
                        theLinePosPtr;
    float               theBarStepValue,
                        theBarValue;
    long                theFileLen,
                        imageLeft = theTiffInfo->rowStrip,
                        imageLineStart,
                        spinCounter = 0;
    StandardFileReply   theFileSpec = (**theImageDocHndl).theImageFileReply; // use local as FSpOpen returns param in spec
    OSErr               error = noErr;
    short               theGWrowBytes,
                        xCount,
                        imageWidth;
    
    thePixMapHndl = GetGWorldPixMap((*theImageDocHndl)->theImageWorld);
    if (PixMap32Bit(thePixMapHndl))
        SaveSetMMUMode(true);
    if (!LockPixels(thePixMapHndl))
        error = kFailedLockPixels;
    if (!error)
    {
        theBarStepValue = (float)kProgressSteps/(*theImageDocHndl)->theImageYSize; 
        theBarValue = theBarStepValue;  //  intialise progress bar value
        error = FSpOpenDF(&theFileSpec.sfFile,fsRdPerm,&theFileSpec.sfFile.vRefNum);
        error |= SetFPos(theFileSpec.sfFile.vRefNum,fsFromStart,theTiffInfo->imageOffset);
        if (!error) 
        {
            imageLinePtr = GetPixBaseAddr(thePixMapHndl);   // base addr of GWorld
            imageLineStart = (long)imageLinePtr;
            theGWrowBytes = (**thePixMapHndl).rowBytes & 0x3FFF;
            // Display the progress bar dialog to provide feedback on load operation.
            DisplayProgressBarDlog(kDisplayProgressWindow, 0 , rProgressMessages, iLoading);
            // Read in imaga data from file into Pixmap
            switch (bitDepth)
            {
                case 8:
                    theFileLen = (**thePixMapHndl).bounds.right - (**thePixMapHndl).bounds.left;
                    watchCrsr = GetCursor(watchCursor);
                    SetCursor(*watchCrsr);
                    do{
                        FSRead(theFileSpec.sfFile.vRefNum,&theFileLen,imageLinePtr);    // get count
                        imageLinePtr+=theGWrowBytes;
                        imageLeft--;
                        theBarValue += theBarStepValue;
                        DisplayProgressBarDlog(kUpdateProgressWindow, (short)theBarValue, 0, 0);
                        RotateCursor(spinCounter+=8);
                    } while (imageLeft);
                    SetCursor(&qd.arrow);
                break;
                case 16:
                    DebugStr("\p16 bit color depth not supported");
                break;
                case 32:
                    theFileLen = 3;
                    imageWidth = (**thePixMapHndl).bounds.right - (**thePixMapHndl).bounds.left;
                    watchCrsr = GetCursor(watchCursor);
                    SetCursor(*watchCrsr);
                    do{
                        theLinePosPtr = imageLinePtr;
                        theLinePosPtr++;
                        for (xCount = 0;xCount<imageWidth;xCount++)
                        {
                            FSRead(theFileSpec.sfFile.vRefNum,&theFileLen,theLinePosPtr);
                            theLinePosPtr+=4;
                        }
                        imageLinePtr+=theGWrowBytes;
                        imageLeft--;
                        theBarValue += theBarStepValue;
                        DisplayProgressBarDlog(kUpdateProgressWindow, (short)theBarValue, 0, 0);
                        RotateCursor(spinCounter+=8);
                    } while (imageLeft);
                    SetCursor(&qd.arrow);
                break;
            }
            UnlockPixels(thePixMapHndl);
            DisplayProgressBarDlog(kDisposeProgressWindow,0,0,0);
        }
        else
            DebugStr("\pError opening and setting file offset in LoadTiffToGWorld");
    }
    SaveSetMMUMode(false);
    return error;
}
 
 
/****   Swap the byte order between little and big endian formats   ****/
 
#pragma segment fileSys
long    SwapByteOrder(long  theData,short   theDataLength)
{
    long    tempStore,
            tempStore2,
            tempStore3,
            tempStore4;
    
    switch(theDataLength)
    {
        case (2):
            tempStore = theData << 8;
            tempStore = tempStore & 0x0000FF00;
            tempStore2 = theData >> 8;
            tempStore2 = tempStore2 & 0x000000FF;
            theData = tempStore + tempStore2;
        break;
        case (4):
            tempStore = theData << 24;
            tempStore = tempStore & 0xFF000000;
            tempStore2 = theData << 8;
            tempStore2 = tempStore2 & 0x00FF0000;
            tempStore3 = theData >> 8;
            tempStore3 = tempStore3 & 0x0000FF00;
            tempStore4 = theData >> 24;
            tempStore4 = tempStore4 & 0x000000FF;
            theData = tempStore + tempStore2 + tempStore3 + tempStore4;
        break;
    }
    return theData;
}
 
 
/****   Load PICT file into PicHandle   ****/
 
// Basic PICT file load function that takes a PICT file
// and returns the loaded image in a PicHandle structure.
// If an error occurs nil is returned to indicate load problem.
 
#pragma segment fileSys
PicHandle   LoadPictImageFile(ImageDocHndl  theDocHndl)
{
    PicHandle           thePictHndl;
    long                thePictFileSize;
    StandardFileReply   theFileSpec = (**theDocHndl).theImageFileReply;
    short               theFileRef;
    OSErr               error = noErr;
    
    error = FSpOpenDF(&theFileSpec.sfFile, fsRdPerm, &theFileRef);
    if (!error)
    {
        error = GetEOF(theFileRef, &thePictFileSize);
        if(error)
        {
            FSClose(theFileSpec.sfFile.vRefNum);
            return nil; 
        }
        if(SetFPos(theFileRef,fsFromStart,512))
        {
            FSClose(theFileRef);
            return nil; 
        }
        thePictFileSize -= 512; // Remove unwanted 512 byte header 
        thePictHndl = (PicHandle)NewHandle(thePictFileSize);
        if(thePictHndl == nil)
        {
            FSClose(theFileRef);
            return nil;
        }
        HLock((Handle)thePictHndl);
        error = FSRead(theFileRef,&thePictFileSize,(Ptr)*thePictHndl);
        HUnlock((Handle)thePictHndl);
        FSClose(theFileRef);
        if(error)
            return nil; 
        return thePictHndl;
    }
    return nil;
}
 
 
/****   Draw PicHandle into GWorld PixMap   ****/
 
// Draw a PICT contained in a PicHandle into a GWorld 
// attached to the passed document structure parameter
 
#pragma segment fileSys
OSErr   DrawPictToGWorld (ImageDocHndl  theDocHndl,PicHandle    thePictHndl)
{
    PixMapHandle    theGWorldPMHndl;
    GDHandle        oldGDHndl;
    CGrafPtr        oldGWorld;
    OSErr           error = noErr;
    
    GetGWorld(&oldGWorld, &oldGDHndl);
    SetGWorld((**theDocHndl).theImageWorld, nil);
    // Reset foreground and background colors to black and white
    // so no colorisation effects occur.
    RGBForeColor(&kRGBBlack);                           
    RGBBackColor(&kRGBWhite);
    theGWorldPMHndl = GetGWorldPixMap((**theDocHndl).theImageWorld);
    LockPixels(theGWorldPMHndl);
    EraseRect(&(**theDocHndl).theImageWorld->portRect);
    // Reset the transfer mode
    PenMode(srcCopy);                                   
    // render the image into the offscreen buffer
    DrawPicture(thePictHndl, &(**theDocHndl).theImageWorld->portRect);
    UnlockPixels(theGWorldPMHndl);
    SetGWorld(oldGWorld, oldGDHndl);
    // dispose of unwanted picHandle structure
    DisposeHandle((Handle)thePictHndl);
    return  error;
}
 
 
/****   Save supported file types   ****/
 
// Main function to perform the saving of supported image file types.
 
#pragma segment fileSys
OSErr   SaveSupportedImageFile(ImageDocHndl theDocHndl)
{
    StandardFileReply   theSFReply;
    DlgHookYDUPP        theDialogHookUPP;
    Point               dlogPosition = {-1,-1};
    OSErr               error = noErr;
    
    gSaveFileType = (*theDocHndl)->theFileType;
    theDialogHookUPP = NewDlgHookYDProc(CustomPutFileHook);
    CustomPutFile((ConstStr255Param)kNull_Str, (**theDocHndl).theImageFileReply.sfFile.name,
                  &theSFReply, rCustomSaveDialog,dlogPosition, theDialogHookUPP,
                  nil, nil, nil, nil);
    DisposeRoutineDescriptor((UniversalProcPtr)theDialogHookUPP);
    if (theSFReply.sfGood)
    {
        /****   Dispose Temp file if present    ****/
        if ((*theDocHndl)->hasUndoTemp || (*theDocHndl)->hasRedoTemp)
            RemoveTempFile(theDocHndl, true, true);
        switch (gSaveFileType)
        {
            case TiffType:
            break;
            case PictType:
                if ( !(*theDocHndl)->isUsingQDGX )
                {
                    error = SavePictImage(theDocHndl, &theSFReply);
                    switch (error)
                    {
                        case kFailPictCompSave:
                            DisplayAlert(rGenAlert,rFileIOMessages,iSaveCompPICTErr);
                        break;
                        case kFailPictUnCompSave:
                            DisplayAlert(rGenAlert,rFileIOMessages,iSaveUnCompPICTErr);
                        break;
                        case kFailedLockPixels:
                            DisplayAlert(rGenAlert,rQDMessages,iFailLockPixmap);
                        break;
                    }
                }
            break;
            case GXType:
                if ( (*theDocHndl)->isUsingQDGX )
                {
                    error = SaveQdGxShape(theDocHndl, &theSFReply);
                    if (error)
                        DisplayAlert(rGenAlert,rFileIOMessages,iSaveQDGXErr);
                }
            break;
        }
    }
    return error;
}
 
 
/****   CustomPutFile   dialog file hook function   ****/
 
#pragma segment fileSys
pascal short    CustomPutFileHook(short item,DialogPtr theDlogPtr,void* theData)
{
    Handle              theHandle;
    Rect                theRect;
    short               theType,
                        ignored;
    
    if (GetWRefCon((WindowPtr)theDlogPtr) != sfMainDialogRefCon)
        return item;
    switch (item)   
    {
        case sfHookFirstCall:
            GetDItem(theDlogPtr,kSaveFilePopUpItem,&theType,&theHandle,&theRect);
            switch (gSaveFileType)
            {
                case TiffType:
                    theType = iSaveTiffType;
                break;
                case PictType:
                    theType = iSavePictType;
                break;
                case GXType:
                    theType = iSaveGXType;
                break;
            }
            SetCtlValue((ControlHandle)theHandle,theType);
            return sfHookNullEvent;
        break;
        case kSaveFilePopUpItem:
            GetDItem(theDlogPtr,item,&ignored,&theHandle,&theRect);
            theType = GetCtlValue((ControlHandle)theHandle);
            switch (theType)
            {
                case iSaveTiffType:
                    gSaveFileType = TiffType;
                break;
                case iSavePictType:
                    gSaveFileType = PictType;
                break;
                case iSaveGXType:
                    gSaveFileType = GXType;
                break;
            }
            return sfHookNullEvent;
        break;
    }
    return item;
}   
 
 
/****   Save image as PICT file     ****/
 
#pragma segment fileSys
OSErr SavePictImage(ImageDocHndl theDocHndl, StandardFileReply* thePictFile)
{
    Handle                  compDataHndl;
    Ptr                     compDataPtr;
    ImageDescriptionHandle  compImageDescHndl = nil;
    PicHandle               theSavePict = nil;
    GDHandle                theGDevHndl;
    PixMapHandle            theSavePictPixMapHndl = nil;
    GWorldPtr               oldPort;
    long                    thePictLen,
                            emptyDataSize = 4,
                            emptyData = 0x00000000,
                            maxCompSize = 0;
    CodecQ                  compQuality;
    Rect                    thePortRect;
    OpenCPicParams          thePictParams;
    OSErr                   error = noErr,
                            fileErr = noErr;
    short                   fileRefNum,
                            compressionSetting,
                            count;  
    
    GetGWorld(&oldPort,&theGDevHndl);
    thePortRect = (**theDocHndl).theImageWorld->portRect;
    theSavePictPixMapHndl = GetGWorldPixMap((**theDocHndl).theImageWorld);
    if (PixMap32Bit(theSavePictPixMapHndl))
        SaveSetMMUMode(true);
    if (LockPixels(theSavePictPixMapHndl))
    {
        /****   setup Color PICT paramaters ****/
        thePictParams.srcRect = thePortRect;
        thePictParams.hRes = ff(72);
        thePictParams.vRes = ff(72);
        thePictParams.version = -2;
        thePictParams.reserved1 = 0;
        thePictParams.reserved2 = 0;
        // Display PICT compression setting dialog box
        compressionSetting = ProcessPictCompDlog();
        if (compressionSetting == kNoCompression)
        {
            
            // Create uncompressed PicHandle ready to save to disk.
            SetGWorld((**theDocHndl).theImageWorld,nil);
            theSavePict = OpenCPicture(&thePictParams);
            ClipRect (&thePortRect);
            CopyBits((BitMap*)(*theSavePictPixMapHndl),
                     (BitMap*)(*theSavePictPixMapHndl),
                     &thePortRect,
                     &thePortRect,srcCopy,nil);
            ClosePicture();
            SetGWorld(oldPort,theGDevHndl);
            UnlockPixels(theSavePictPixMapHndl);
        }
        else    
        {
            // Image compression selected. Compress image using chosen setting
            // and create PicHandle containing the compressed image ready to save to disk
            switch (compressionSetting)
            {
                case kHighQuality:
                    compQuality = codecHighQuality;
                break;
                case kMediumQuality:
                    compQuality = codecNormalQuality;
                break;
                case kLowQuality:
                    compQuality = codecLowQuality;
                break;
            }
            error = GetMaxCompressionSize(theSavePictPixMapHndl,
                                        &thePortRect,0,compQuality,
                                        'jpeg',anyCodec,&maxCompSize);
            if (error != noErr || maxCompSize == 0)
            {
                UnlockPixels(theSavePictPixMapHndl);
                SaveSetMMUMode(false);
                return kDefaultAppError;
            }
            compImageDescHndl = (ImageDescriptionHandle) NewHandle(4);
            compDataHndl = NewHandle(maxCompSize);
            if (compImageDescHndl != nil && compDataHndl != nil)
            {
                HLock(compDataHndl);
                compDataPtr = StripAddress(*compDataHndl);
                error = CompressImage (theSavePictPixMapHndl,&thePortRect,
                                       compQuality,'jpeg',compImageDescHndl,compDataPtr);
                if (error != noErr)
                {
                    RemoveOldSaveData(compImageDescHndl,compDataHndl,theSavePictPixMapHndl);
                    SaveSetMMUMode(false);
                    return kFailPictCompSave;
                }
                SetGWorld((**theDocHndl).theImageWorld,nil);
                theSavePict = OpenCPicture(&thePictParams);
                ClipRect (&thePortRect);
                error = DecompressImage(compDataPtr, compImageDescHndl,
                                        theSavePictPixMapHndl,&thePortRect,
                                        &thePortRect, srcCopy, nil);
                ClosePicture();
                SetGWorld(oldPort,theGDevHndl);
                RemoveOldSaveData(compImageDescHndl,compDataHndl,theSavePictPixMapHndl);
                if (error)
                {
                    if (theSavePict)
                        DisposeHandle((Handle)theSavePict);
                    SaveSetMMUMode(false);
                    return kFailPictCompSave;
                }
            }
            else
            {
                RemoveOldSaveData(compImageDescHndl,compDataHndl,theSavePictPixMapHndl);
                SaveSetMMUMode(false);
                return kFailPictCompSave;
            }
        }
    }
    else
        return kFailedLockPixels;   
    // Save image conatined in PicHandle structure to disk
    fileErr = FSpCreate(&thePictFile->sfFile,'ttxt','PICT',smSystemScript);
    if (!fileErr)
        fileErr = FSpOpenDF(&thePictFile->sfFile,fsRdWrPerm,&fileRefNum);
    if (!fileErr)
    {
        for (count = 0;count<128;count++)
            FSWrite(fileRefNum,&emptyDataSize,&emptyData);
        thePictLen = GetHandleSize((Handle)theSavePict);
        HLock((Handle)theSavePict);
        fileErr = FSWrite(fileRefNum,&thePictLen,*theSavePict);
        fileErr |= FSClose(fileRefNum);
        HUnlock((Handle)theSavePict);
        if (fileErr)
        {
            switch (compressionSetting)
            {
                case kNoCompression:
                    error = kFailPictUnCompSave;
                break;
                default:
                    error = kFailPictCompSave;
                break;
            }
        }
    }
    SaveSetMMUMode(false);
    if (theSavePict)
        DisposeHandle((Handle)theSavePict);
    return error;
}
 
 
/****   handle processing of the PICT file settings dialog  ****/
 
#pragma segment fileSys
short   ProcessPictCompDlog(void)
{
    Handle              theItemHandle;
    DialogPtr           theDialog = nil;
    ModalFilterUPP      theFilter = nil;
    GrafPtr             oldPort;
    Rect                theRect;
    short               theCompSetting = kNoCompression;
    short               theItem,
                        theNextItem,
                        theItemType,
                        count;
    
    GetPort(&oldPort);
    theDialog = GetNewDialog(rPictCompDialog,nil,((WindowPtr)-1L));
    SetPort(theDialog);
    if (!GetStdFilterProc(&theFilter))
        SetDialogDefaultItem(theDialog,1);
    GetDialogItem(theDialog,theCompSetting,&theItemType,&theItemHandle,&theRect);
    SetControlValue((ControlHandle)theItemHandle,1);
    do{
        ModalDialog(nil,&theItem);
        GetDialogItem(theDialog,theItem,&theItemType,&theItemHandle,&theRect);
        if (theItemType == kRadioButton)
        {
            for (count = kNoCompression; count<=kLowQuality; count++)
            {
                GetDialogItem(theDialog,count,&theItemType,&theItemHandle,&theRect);
                if (count == theItem)
                {
                    SetControlValue((ControlHandle)theItemHandle,1);
                    theCompSetting = count;
                }
                else
                    SetControlValue((ControlHandle)theItemHandle,0);
            }
        }
    } while (theItem != kOkButton);
    DisposeDialog(theDialog);
    SetPort(oldPort);
    return  theCompSetting;
}
 
 
/****   Clean up after save file    ****/
 
#pragma segment fileSys
void    RemoveOldSaveData(ImageDescriptionHandle theImageDescH, Handle theDataH, PixMapHandle thePixMapHndl)
{
    UnlockPixels(thePixMapHndl);
    if (theImageDescH)
        DisposeHandle((Handle)theImageDescH);
    if (theDataH)
    {
        HUnlock(theDataH);
        DisposeHandle(theDataH);
    }
}
 
 
/****   convert QuickDraw GX shape to handle for saving to disk ****/
 
#pragma segment fileSys
void    SaveGXShapeToFile(gxShape theShape, gxFlattenFlag theFlags, short fileRefNum)
{
    UserGXSpool     dataBlock;
    
    dataBlock.spool.spoolProcedure = NewgxSpoolProc(FileSpoolProc);
    dataBlock.reference = fileRefNum;
    dataBlock.spool.buffer = nil;
    dataBlock.spool.bufferSize = 0;
    GXFlattenShape(theShape, theFlags, &dataBlock.spool);
    DisposeRoutineDescriptor(dataBlock.spool.spoolProcedure);
}
 
 
/****   Save flatten QD GX shape to disk    ****/
 
#pragma segment fileSys
OSErr   SaveQdGxShape(ImageDocHndl theDocHndl, StandardFileReply* thePictFile)
{
    OSErr       fileErr = noErr;
    short       theRefNum;
    
    if ((*theDocHndl)->theGXImageShape == nil)
        return kFailQDGXSave;
    fileErr = FSpCreate(&thePictFile->sfFile,(OSType)AppCreator,'qdgx',smSystemScript);
    if (!fileErr)
        fileErr = FSpOpenDF(&thePictFile->sfFile,fsRdWrPerm,&theRefNum);
    if (fileErr)
        DebugStr("\pFile error creating QDGX file for save operation");
    SaveGXShapeToFile((*theDocHndl)->theGXImageShape, 0,theRefNum);
    fileErr |= FSClose(theRefNum);
    if (fileErr)
        fileErr = kFailQDGXSave;
    return fileErr;
}
 
 
/**** QuickDraw GX shape to file Spooling function  ****/
 
#pragma segment fileSys
static long FileSpoolProc(gxSpoolCommand command, UserGXSpool *block)
{
   OSErr        error = noErr;
   
   switch (command)
   {
      case gxOpenReadSpool:
      break;
      case gxOpenWriteSpool:
      break;
      case gxReadSpool:
      break;
      case gxWriteSpool:
      {
         long count = block->spool.count;
         error = FSWrite(block->reference, &count, block->spool.buffer);
         if (error)
            DebugStr("\pFSWrite failed");
      }
      break;
      case gxCloseSpool:
      break;
      default:
         DebugStr("\punexpected spool command");
      break;
   }
   return noErr;
}
 
 
/****   Revert image to last saved image ****/
 
#pragma segment fileSys
OSErr   RevertToSavedFile(ImageDocHndl theDocHndl, WindowPtr theWindow)
{
    PicHandle           thePictHndl = nil;
    PictInfo            thePictInfo;
    GrafPtr             oldPort;
    TiffInfo            theTiffStruct;
    OSErr               error = noErr;
    
    (**theDocHndl).theImageChanged = false;
    if ((**theDocHndl).hasUndoTemp)
    {
        RemoveTempFile(theDocHndl, true, false);
        (**theDocHndl).hasUndoTemp = false;
    }
    if ((**theDocHndl).hasRedoTemp)
    {
        RemoveTempFile(theDocHndl, false, true);
        (**theDocHndl).hasRedoTemp = false;
    }
    (**theDocHndl).theUndoState = kCannotUndo;
    if ((**theDocHndl).theImageWorld)
        DisposeGWorld((**theDocHndl).theImageWorld);
    if ((**theDocHndl).theColorsPalette)
        DisposePalette((**theDocHndl).theColorsPalette);
    if ((**theDocHndl).isUsingQDGX)
    {
        GXDisposeShape((**theDocHndl).theGXImageShape);
        GXDisposeInk((**theDocHndl).theInkShape);
        GXDisposeViewPort((**theDocHndl).theGxChildView);
        GXDisposeViewPort((**theDocHndl).theGXview);
    }
    switch ((*theDocHndl)->theFileType)
    {
        case TiffType:
            theTiffStruct.tiffCTabHndl = nil;       // set to nil as Color table not always created
            GetTIFFHdrInfo(theDocHndl,&theTiffStruct);
            GetTIFFIFDirectory(theDocHndl,&theTiffStruct);
            switch (theTiffStruct.bitDepth)
            {
                case 8:
                    (**theDocHndl).theImageWorld = 
                        CreateOffscreen(theTiffStruct.tiffCTabHndl,
                                        theTiffStruct.xImageSize,
                                        theTiffStruct.yImageSize,
                                        theTiffStruct.bitDepth,kNoFlags);
                    if ((**theDocHndl).theImageWorld == nil)
                    {
                        DisposeHandle((Handle)theDocHndl);
                        return kFailMakeGWorld;
                    }
                break;
                case 32:
                    (**theDocHndl).theImageWorld = 
                        CreateOffscreen(nil,
                                        theTiffStruct.xImageSize,
                                        theTiffStruct.yImageSize,
                                        theTiffStruct.bitDepth,kNoFlags);
                    if ((**theDocHndl).theImageWorld == nil)
                    {   
                        DisposeHandle((Handle)theDocHndl);
                        return kFailMakeGWorld;
                    }
                break;
                default:
                    DisplayAlert (rGenWarning,rErrMessages,iNoSupportBitDepth);
                    return kBitDepthErr;
                break;
            }
            error = LoadTiffToGWorld (theDocHndl,&theTiffStruct,theTiffStruct.bitDepth);
            if (error)
            {
                DisplayAlert (rGenAlert,0,0);
                return error;
            }
            SetUndoItemText((*theDocHndl)->theUndoState);
        break;
        case PictType:
            thePictHndl = LoadPictImageFile(theDocHndl);
            if (!thePictHndl)
            {
                DisposeHandle((Handle)theDocHndl);
                return kDefaultAppError;
            }
            // test if minimal temp memory free available. If none available GetPictInfo() fails.
            if (TempFreeMem() < kMinLowMem)
            {
                DisplayAlert(rGenWarning,rErrMessages,iLowTempMem);
                DisposeHandle((Handle)theDocHndl);
                return kLowMemWarning;
            }
            error = GetPictInfo (thePictHndl, &thePictInfo, returnColorTable,256,systemMethod,0);
            if (!error && thePictInfo.depth <= 8)
                (**theDocHndl).theImageWorld = 
                    CreateOffscreen(thePictInfo.theColorTable,
                                    (**theDocHndl).theImageXSize,
                                    (**theDocHndl).theImageYSize,
                                    (**theDocHndl).theImageDepth,kNoFlags);
            else
                (**theDocHndl).theImageWorld = 
                    CreateOffscreen(nil,
                                    (**theDocHndl).theImageXSize,
                                    (**theDocHndl).theImageYSize,
                                    (**theDocHndl).theImageDepth,kNoFlags);
            
            if ((**theDocHndl).theImageWorld == nil)
            {
                DisposeHandle((Handle)theDocHndl);      // dispose image document
                DisposeHandle((Handle)thePictHndl);     // dispose picHandle
                return kFailMakeGWorld;
            }
            error = DrawPictToGWorld (theDocHndl,thePictHndl);
            SetUndoItemText((*theDocHndl)->theUndoState);
        break;
        case GXType:
            DisplayAlert(rGenWarning,rErrMessages,iNotImplemented);
            return noErr;
        break;
    }
    GetPort(&oldPort);
    SetPort(theWindow);
    InvalRect(&theWindow->portRect);
    SetPort(oldPort);
    return error;
}
 
 
/****   Clean up after abort TIFF load  ****/
 
#pragma segment fileSys
void CleanLoadAbort(ImageDocHndl theDocHndl)
{
    if ((**theDocHndl).theColorsPalette)
        DisposePalette((**theDocHndl).theColorsPalette);
    DisposeHandle((Handle)theDocHndl);
}