Common Files/ImageCompressionUtilities.c

//////////
//
//  File:       ImageCompressionUtilities.c
//
//  Contains:   Utilities for compressing images.
//
//  Written by: Peter Hoddie, Sean Allen, Chris Flick
//  Revised by: Tim Monroe
//
//  Copyright:  © 1997-2000 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <8>      11/17/00    rtm     general clean-up to bring this file into conformance with style of other
//                                  sample code
//     <7>      03/28/00    rtm     changed ((**myPixMap).rowBytes & 0x3fff) to QTGetPixMapHandleRowBytes(myPixMap)
//     <6>      03/17/00    rtm     moved some things to ImageCompressionUtilities.h; made some changes for Carbon
//     <5>      02/25/00    rtm     changed pascal keyword to PASCAL_RTN, for Windows compatibility
//     <4>      12/16/98    rtm     removed orphaned prototype for compressTransparentRLEwithHitTesting
//     <3>      05/28/98    rtm     added some typecasting to minimize MSDev compiler warnings
//     <2>      04/22/98    rtm     made changes to ICUtils_RecompressPictureFileWithTransparency, as per Chris' fixes
//     <1>      03/27/98    rtm     existing file
//
//////////
 
#ifndef __IMAGECOMPRESSIONUTILITIES__
#include "ImageCompressionUtilities.h"
#endif
 
 
//////////
//
// ICUtils_CreateImageDescription
// Create an image description.
//
//////////
 
static ImageDescriptionHandle ICUtils_CreateImageDescription (CodecType theCodecType, PixMapHandle thePixmap)
{
    ImageDescriptionHandle  myDesc = NULL;
    OSErr                   myErr = noErr;
    
    if (thePixmap == NULL)
        return(myDesc);
    
    myDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
    if (myDesc != NULL) {
        Rect                myRect = (**thePixmap).bounds;
 
        (**myDesc).idSize           = sizeof(ImageDescription);
        (**myDesc).cType            = theCodecType; 
        (**myDesc).spatialQuality   = codecNormalQuality;
        (**myDesc).width            = myRect.right - myRect.left;
        (**myDesc).height           = myRect.bottom - myRect.top;
        (**myDesc).hRes             = 72L << 16;
        (**myDesc).vRes             = 72L << 16;
        (**myDesc).dataSize         = (QTGetPixMapHandleRowBytes(thePixmap)) * (**myDesc).height;
        (**myDesc).depth            = (**thePixmap).pixelSize;
        (**myDesc).clutID           = -1;
        
        myErr = SetImageDescriptionCTable(myDesc, (**thePixmap).pmTable);
        if (myErr != noErr) {
            DisposeHandle((Handle)myDesc);
            myDesc = NULL;
        }
    }
        
    return(myDesc);
}
 
 
//////////
//
// ICUtils_NoDitherBitsProc
//
//////////
 
PASCAL_RTN void ICUtils_NoDitherBitsProc (const BitMap *theSrcBits, const Rect *theSrcRect, const Rect *theDstRect, short theMode, RgnHandle theMaskRgn)
{
    theMode &= ~ditherCopy;
    StdBits(theSrcBits, theSrcRect, theDstRect, theMode, theMaskRgn);
}
 
 
//////////
//
// ICUtils_DrawPictureNoDither
// Draw a QuickTime picture, but convert any use of ditherCopy graphics modes to graphics modes that don't use ditherCopy.
//
//////////
 
static void ICUtils_DrawPictureNoDither (PicHandle thePicture, const Rect *theRect)
{       
    CQDProcs        myProcs;
    GrafPtr         mySavedPort;
    CQDProcsPtr     mySavedProcs;
 
    GetPort(&mySavedPort);
    mySavedProcs = GetPortGrafProcs(mySavedPort);
    SetStdCProcs(&myProcs);
 
    myProcs.bitsProc = NewQDBitsUPP(ICUtils_NoDitherBitsProc);
    SetPortGrafProcs((CGrafPtr)mySavedPort, &myProcs);
 
    DrawPicture(thePicture, theRect);
 
    SetPortGrafProcs((CGrafPtr)mySavedPort, mySavedProcs);
    DisposeQDBitsUPP(myProcs.bitsProc);
}
 
 
//////////
//
// ICUtils_PrepareFor16BitCompress
//
// Clean up the colors in a picture before being compressed with the Animation compressor in 16-bits at codecNormal quality.
//
// The QuickTime Animation compressor can operate in both a lossless and lossy manner. This routine is meant to help when
// compressing in a lossy manner. Here, "clean up" means to force any colors that are sufficiently close to white to colors
// that are not as close. Since the Animation compressor will perform thresholding of the image's colors when codecNormal
// quality is specified, without this preprocessing, colors very close to white can be mapped to white in the compression.
// If white was chosen as the key color, pixels that are close to white could possibly become transparent as well.
//
// While not used by other routines in this file, ICUtils_PrepareFor16BitCompress is provided as an example of how this might be done.
//
// Note that with codecLossless quality, there is no remapping of colors so this step is unnecessary.
//
//////////
 
static OSErr ICUtils_PrepareFor16BitCompress (PicHandle *thePicture)
{
    PicHandle               myPicture = NULL;
    Rect                    myRect;
    GWorldPtr               myGWorld = NULL;
    CGrafPtr                mySavedPort;
    GDHandle                mySavedDevice;
    short                   myRowBytes;
    Ptr                     myBaseAddr;
    long                    w, h;
    PixMapHandle            myPixMap;
    OSErr                   myErr = noErr;
 
    GetGWorld(&mySavedPort, &mySavedDevice);
 
    myRect = (***thePicture).picFrame;
    MacOffsetRect(&myRect, (short)-myRect.left, (short)-myRect.top);
 
    myErr = QTNewGWorld(&myGWorld, 32, &myRect, NULL, NULL, kICMTempThenAppMemory);
    if (myErr != noErr)
        goto bail;
 
    myPixMap = GetGWorldPixMap(myGWorld);
    LockPixels(myPixMap)
    ;
    SetGWorld(myGWorld, NULL);
    EraseRect(&myRect);
 
    DrawPicture(*thePicture, &myRect);
 
    myBaseAddr = GetPixBaseAddr(myPixMap);
    myRowBytes = QTGetPixMapHandleRowBytes(myPixMap);;
    for (h =0 ; h < myRect.bottom; h++) {
        long *myPixelPtr = (long *)myBaseAddr;
 
        for (w = 0; w < myRect.right; w++, myPixelPtr++) {
            UInt8 r, g, b, a;
            long myPixel = *myPixelPtr;
 
            if ((myPixel & 0x0ffffff) == 0x00ffffff)        // pure white
                continue;
 
            a = (myPixel >> 24) & 0x0ff;
            r = (myPixel >> 16) & 0x0ff;
            g = (myPixel >>  8) & 0x0ff;
            b = (myPixel >>  0) & 0x0ff;
            if ((r > kThreshold) && (g > kThreshold) && (b > kThreshold)) {
                r = g = b = kThreshold;
                *myPixelPtr = (a << 24) | (kThreshold << 16) | (kThreshold << 8) | (kThreshold << 0);
            }
        }
 
        myBaseAddr += myRowBytes;
    }
 
    myPicture = OpenPicture(&myRect);
    CopyBits((BitMapPtr)*GetGWorldPixMap(myGWorld),
             (BitMapPtr)*GetGWorldPixMap(myGWorld),
             &myRect,
             &myRect,
             ditherCopy,
             NULL);
             
    ClosePicture();
    UnlockPixels(myPixMap);
 
bail:
    if (myGWorld)
        DisposeGWorld(myGWorld);
 
    if (myErr == noErr) {
        if (myPicture) {
            KillPicture(*thePicture);
            *thePicture = myPicture;
        }
    }
 
    SetGWorld(mySavedPort, mySavedDevice);
 
    return(myErr);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Callback-based routines for compressing with hit-testing and transparency.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// ICUtils_RecompressWithTransparencyFromProc
//
// This routine recompresses some given image data with transparency, using the specified key color.
// It uses a callback procedure (theDrawProc) to get the dimensions of the area of the drawable entity
// and for actually drawing that entity.
//
// This routine is called by _RecompressCompressedImageWithTransparency, _RecompressPictureWithTransparency,
// and _RecompressPictureFileWithTransparency; you can also call it directly. 
//
// Here's a recap of the parameters:
//
//  theDrawProc             the callback procedure that gets area of image and draws it
//  theDrawProcRefCon       a reference constant passed to theDrawProc
//  includeHitTesting       TRUE if hit-testing data should be added to the compressed data
//  theKeyColor             an RGBColor that is to be used as the key color; NULL if no tansparency
//  theHitTestRegion        a RgnHandle specifying an area that is to be used as the hit test area;
//                          if you pass this, you must also set includeHitTesting to TRUE; this is
//                          optional, as the callback procedure can perform drawing of the hit test area
//                          (which is often suitable when both the image and hit test area were painted
//                          in a drawing program)
//  theImageDesc            returned ImageDescriptionHandle for the compressed image data
//  theImageData            returned Handle to the compressed image data
// 
//
//////////
 
OSErr ICUtils_RecompressWithTransparencyFromProc (
                                        CompressDrawProc theDrawProc,
                                        void *theDrawProcRefCon, 
                                        Boolean includeHitTesting,
                                        RGBColor *theKeyColor, 
                                        RgnHandle theHitTestRegion,
                                        ImageDescriptionHandle *theImageDesc,
                                        Handle *theImageData)
{
    Rect                        myRect;
    CGrafPtr                    mySavedPort;
    GDHandle                    mySavedDevice;
    GWorldPtr                   myGWImage = NULL;       // always used
    GWorldPtr                   myGWPrev = NULL;        // used if compressing with transparency (via key color)
    GWorldPtr                   myGWMap = NULL;         // used if compressing with hittesting data
    ImageDescriptionHandle      myDesc = NULL;          // resulting image description
    ImageDescriptionHandle      myGWMapDesc = NULL;
    ImageSequence               mySeqID = 0;            // compression sequence
    ImageSequenceDataSource     myMapSource = 0L;
    Ptr                         myData = NULL;
    long                        myDataSize;
    UInt8                       mySimilarity;
    Boolean                     includeTransparency = false;
    RGBColor                    mySaveBackColor;
    OSErr                       myErr = noErr;
    
    // get the current graphics port and device
    GetGWorld(&mySavedPort, &mySavedDevice);
    
    if ((theDrawProc == NULL) || (theImageDesc == NULL) || (theImageData == NULL)) {
        myErr = paramErr;
        goto bail;
    }
    
    //////////
    //
    // allocate and configure any storage we'll need for recompressing the image data
    //
    //////////
    
    // tell the callback procedure that it should initialize any storage it needs
    myErr = theDrawProc(kRecoProcInitMsg, NULL, NULL, 0, theDrawProcRefCon);
    if (myErr != noErr)
        goto bail;
    
    // determine the bounds to use for compression
    myErr = theDrawProc(kRecoProcGetBoundsMsg, &myRect, NULL, 0, theDrawProcRefCon);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTNewGWorld(&myGWImage, kCompressDepth, &myRect, NULL, NULL, kICMTempThenAppMemory);
    if (myErr != noErr)
        goto bail;
 
    if (theKeyColor != NULL)
        includeTransparency = true;
    
    // if we are to include transparency, we need a previous buffer and an 8-bit GWorld for the mask data
    if (includeHitTesting) {
        myErr = QTNewGWorld(&myGWPrev, kCompressDepth, &myRect, NULL, NULL, kICMTempThenAppMemory);
        if (myErr != noErr)
            goto bail;
 
        myErr = QTNewGWorld(&myGWMap, 8, &myRect, NULL, NULL, kICMTempThenAppMemory);
        if (myErr != noErr)
            goto bail;
    }
    
    // lock our GWorld pixmaps
    LockPixels(GetGWorldPixMap(myGWImage));
    
    if (myGWPrev != NULL)
        LockPixels(GetGWorldPixMap(myGWPrev));
    
    if (myGWMap != NULL)
        LockPixels(GetGWorldPixMap(myGWMap));
    
    //////////
    //
    // set up the GWorlds
    //
    //////////
    
    // erase the previous GWorld
    if (myGWPrev != NULL) {
        SetGWorld(myGWPrev, NULL);
        ClipRect(&myRect);
        
        GetBackColor(&mySaveBackColor);
        if (theKeyColor != NULL)
            RGBBackColor(theKeyColor);
        
        EraseRect(&myRect);
        RGBBackColor(&mySaveBackColor);
    }
    
    // draw the hit-test region in the hit-test GWorld
    if (myGWMap != NULL) {
        SetGWorld(myGWMap, NULL);
        
        // paint background white
        EraseRect(&myRect);
 
        myErr = theDrawProc(kRecoProcDrawMsg, &myRect, myGWMap, kRecoProcHitTestingImageType, theDrawProcRefCon);
        if (myErr != noErr)
            goto bail;
        
        // paint the hit-test region
        if (theHitTestRegion != NULL) {
            RGBColor myBlackRGB;
            
            myBlackRGB.red = myBlackRGB.green = myBlackRGB.blue = 0;
            
            RGBForeColor(&myBlackRGB);
            MacPaintRgn(theHitTestRegion);
        }
 
        myGWMapDesc = ICUtils_CreateImageDescription(kRawCodecType, GetGWorldPixMap(myGWMap));
        myErr = MemError();
        if (myErr) goto bail;
    }
    
    // erase the image GWorld
    SetGWorld(myGWImage, NULL);
    ClipRect(&myRect);
    EraseRect(&myRect);
    
    //////////
    //
    // set up the compression sequence
    //
    // we pass codecLosslessQuality so that the key color (if any) is matched exactly; this avoids colors within
    // some threshhold different from the key color being taken as equivalent to the key color; alternatively,
    // we could perform some threshold processing on the source image's pixels and pass codecNormalQuality
    //
    //////////
 
    myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
 
    if (includeHitTesting) {
        // allocate a compression sequence and add source data for hit-test mask
        myErr = CompressSequenceBegin(&mySeqID, GetGWorldPixMap(myGWPrev), NULL, NULL, NULL, kCompressDepth, kAnimationCodecType, 
                                        anyCodec, codecLosslessQuality, codecLosslessQuality, 2, NULL, 0, myDesc);
 
        // with hit-testing, we have to add a data source to hold the mask data
        myErr = CDSequenceNewDataSource(mySeqID, &myMapSource, kRecoProcHitTestingImageType, 1, (Handle)myGWMapDesc, NULL, NULL);
        if (myErr != noErr)
            goto bail;
 
        myErr = CDSequenceSetSourceData(myMapSource, GetPixBaseAddr(GetGWorldPixMap(myGWMap)), (**myGWMapDesc).dataSize);
        if (myErr != noErr)
            goto bail;
 
        // what's the maximum size the compressed data could be (including hit-test data)?
        myErr = GetCSequenceMaxCompressionSize(mySeqID, GetGWorldPixMap(myGWPrev), &myDataSize);
    } else {
        // we're not hit-testing, so we only need the image buffer
        myErr = CompressSequenceBegin(&mySeqID, GetGWorldPixMap(myGWImage), NULL, &myRect, NULL, kCompressDepth, kAnimationCodecType,
                                        0, codecLosslessQuality, codecLosslessQuality, 2, NULL, 0, myDesc);
        if (myErr != noErr)
            goto bail;
        
        // What's the maximum size the compressed data could be?
        myErr = GetCSequenceMaxCompressionSize(mySeqID, GetGWorldPixMap(myGWImage), &myDataSize);
    }
    
    if (myErr != noErr)
        goto bail;
    
    //////////
    //
    // compress the image data
    //
    //////////
 
    myData = NewPtr(myDataSize);
    myErr = MemError();
    if (myErr != noErr)
        goto bail;
    
    if (includeHitTesting) { /* with or without transparency */
        // with hit-testing, we use two buffers; actually we don't have to but do so to show how it can be done
        // (this code was based upon some older code that did)
        
        // compress the GWorld painted with the theKeyColor exclusively
        myErr = CompressSequenceFrame(mySeqID, GetGWorldPixMap(myGWPrev), NULL, 0, myData, &myDataSize, &mySimilarity, NULL);
        if (myErr != noErr)
            goto bail;
 
        myErr = SetCSequencePrev(mySeqID, GetGWorldPixMap(myGWPrev), NULL);
        if (myErr != noErr)
            goto bail;
 
        // draw the image into the GWorld over the area painted with theKeyColor, so that if the picture is already transparent,
        // the areas it doesn't paint will be in the key color
        SetGWorld(myGWImage, NULL);
        
        GetBackColor(&mySaveBackColor);
        if (theKeyColor != NULL)
            RGBBackColor(theKeyColor);
            
        EraseRect(&myRect);
        RGBBackColor(&mySaveBackColor);
    
        myErr = theDrawProc(kRecoProcDrawMsg, &myRect, myGWImage, kRecoProcOriginalImageType, theDrawProcRefCon);
        if (myErr != noErr)
            goto bail;
 
        // now compress the GWorld holding the image drawn on top of the theKeyColor
        myErr = CompressSequenceFrame(mySeqID, GetGWorldPixMap(myGWImage), NULL, 0, myData, &myDataSize, &mySimilarity, NULL);
        if (myErr != noErr)
            goto bail;
        
        // at this point, myData points to the image data for just the difference between the two (thus generating transparency);
        // also, hit-testing data is contained in the image data if so specified
    } else if (includeTransparency) {
        // for transparency without hit-testing, we get by with only using a single buffer
    
        // compress the GWorld painted with the theKeyColor exclusively
        myErr = CompressSequenceFrame(mySeqID, GetGWorldPixMap(myGWImage), NULL, codecFlagUpdatePrevious, myData, &myDataSize, &mySimilarity, NULL);
        if (myErr != noErr)
            goto bail;
 
        // draw the image into the GWorld over the area painted with theKeyColor, so that if the picture is already transparent,
        // the areas it doesn't paint will be in the key color
        SetGWorld(myGWImage, NULL);
        
        GetBackColor(&mySaveBackColor);
        if (theKeyColor != NULL)
            RGBBackColor(theKeyColor);
            
        EraseRect(&myRect);
        RGBBackColor(&mySaveBackColor);
    
        myErr = theDrawProc(kRecoProcDrawMsg, &myRect, myGWImage, kRecoProcOriginalImageType, theDrawProcRefCon);
        if (myErr != noErr)
            goto bail;
 
        // now compress the GWorld holding the image drawn on top of the theKeyColor
        myErr = CompressSequenceFrame(mySeqID, GetGWorldPixMap(myGWImage), NULL, codecFlagUpdatePrevious, myData, &myDataSize, &mySimilarity, NULL);
        if (myErr != noErr)
            goto bail;
        
        // at this point, myData points to the image data for just the difference between the two (thus generating transparency) 
        // also, hit-testing data is contained in the image data if so specified
    } else {
        // no hit-testing and no transparency....
        SetGWorld(myGWImage, NULL);
 
        // draw the image into the GWorld
        myErr = theDrawProc(kRecoProcDrawMsg, &myRect, myGWImage, kRecoProcOriginalImageType, theDrawProcRefCon);
        if (myErr != noErr)
            goto bail;
 
        // compress the GWorld containing the image painted on white
        myErr = CompressSequenceFrame(mySeqID, GetGWorldPixMap(myGWImage), NULL, 0, myData, &myDataSize, &mySimilarity, NULL);
        if (myErr != noErr)
            goto bail;
        
        // at this point, myData points to the image data for just the image, newly compressed; also, hit - data is contained
        // in the image data if it was specified
    }
    
    //////////
    //
    // end the compression sequence and clean up
    //
    //////////
 
    CDSequenceEnd(mySeqID);
    mySeqID = 0;
    
    // free the GWorlds and drop references so we have more memory for PtrToHand
    if (myGWImage != NULL)
        DisposeGWorld(myGWImage);
    myGWImage = NULL;
    
    if (myGWMap != NULL)
        DisposeGWorld(myGWMap);
    myGWMap = NULL;
    
    if (myGWPrev != NULL)
        DisposeGWorld(myGWPrev);
    myGWPrev = NULL;
    
    // return the image data and the image description to the caller
    myErr = PtrToHand(myData, theImageData, myDataSize);
    if (myErr) goto bail;
    
    *theImageDesc = myDesc;
    myDesc = NULL;              // forget about this name for ImageDescriptionHandle so dispose below doesn't catch it
    
bail:
    // tell callback to dispose of anything it allocated; we pass 'err ' in theImageType if an error occurred
    theDrawProc(kRecoProcDisposeMsg, NULL, NULL, myErr ? FOUR_CHAR_CODE('err ') : 0, theDrawProcRefCon);
    
    if (mySeqID != 0)
        CDSequenceEnd(mySeqID);
    
    // restore the original graphics port and device
    SetGWorld(mySavedPort, mySavedDevice);
    
    if (myGWImage != NULL)
        DisposeGWorld(myGWImage);
        
    if (myGWMap != NULL)
        DisposeGWorld(myGWMap);
        
    if (myGWPrev != NULL)
        DisposeGWorld(myGWPrev);
        
    if (myDesc != NULL)
        DisposeHandle((Handle) myDesc);
        
    if (myGWMapDesc != NULL)
        DisposeHandle((Handle) myGWMapDesc);
        
    if (myData != NULL)
        DisposePtr(myData);
        
    return(myErr);
}
 
 
//////////
//
// ICUtils_PictureCompressDrawProc
//
// Helper routine to be used with ICUtils_RecompressWithTransparencyFromProc to compress QuickDraw pictures.
//
//////////
 
static PASCAL_RTN OSErr ICUtils_PictureCompressDrawProc (short theMessage, Rect *theRect, GWorldPtr theDrawingPort, OSType theImageType, void *theRefCon)
{
#if TARGET_OS_MAC
#pragma unused(theDrawingPort)
#endif
    ICUtils_PictureCompressProcData     *myData = theRefCon;
    Rect                                myRect;
    OSErr                               myErr = noErr;
    
    switch (theMessage) {
        case kRecoProcInitMsg:
            break;
            
        case kRecoProcDisposeMsg:
            break;
            
        case kRecoProcGetBoundsMsg:
            myRect = (**myData->picture).picFrame;
            
            myRect.left = EndianS16_BtoN(myRect.left);
            myRect.top = EndianS16_BtoN(myRect.top);
            myRect.bottom = EndianS16_BtoN(myRect.bottom);
            myRect.right = EndianS16_BtoN(myRect.right);
            MacOffsetRect(&myRect, (short)-myRect.left, (short)-myRect.top);
            
            *theRect = myRect;
            break;
            
        case kRecoProcDrawMsg:
            myRect = (**myData->picture).picFrame;
            
            myRect.left = EndianS16_BtoN(myRect.left);
            myRect.top = EndianS16_BtoN(myRect.top);
            myRect.bottom = EndianS16_BtoN(myRect.bottom);
            myRect.right = EndianS16_BtoN(myRect.right);
            MacOffsetRect(&myRect, (short)-myRect.left, (short)-myRect.top);
 
            if (theImageType == kRecoProcOriginalImageType)
                ICUtils_DrawPictureNoDither(myData->picture, &myRect);
            break;
            
        default:
            myErr = -1;
    }
 
    return(myErr);
}   
    
 
//////////
//
// ICUtils_ImageCompressDrawProc
//
// Helper routine to be used with ICUtils_RecompressWithTransparencyFromProc to compress QuickTime compressed image data.
//
//////////
 
static PASCAL_RTN OSErr ICUtils_ImageCompressDrawProc (short theMessage, Rect *theRect, GWorldPtr theDrawingPort, OSType theImageType, void *theRefCon)
{
#if TARGET_OS_MAC
#pragma unused(theImageType)
#endif
 
    ICUtils_CompressedImageCompressProcData     *myData = theRefCon;
    Rect                                        myRect;
    OSErr                                       myErr = noErr;
    
    switch(theMessage) {
        case kRecoProcInitMsg:
            break;
            
        case kRecoProcDisposeMsg:
            break;
            
        case kRecoProcGetBoundsMsg:
            myRect.left = myRect.top = 0;
            myRect.right = (**myData->imageDesc).width;
            myRect.bottom = (**myData->imageDesc).height;
            
            *theRect = myRect;
            break;
            
        case kRecoProcDrawMsg:
            {
                SignedByte      mySavedState;
                
                myRect.left = myRect.top = 0;
                myRect.right = (**myData->imageDesc).width;
                myRect.bottom = (**myData->imageDesc).height;
                
                mySavedState = HGetState(myData->imageData);
                HLockHi(myData->imageData);
                
                if (theImageType == kRecoProcOriginalImageType)
                    myErr = DecompressImage(*myData->imageData, myData->imageDesc, GetGWorldPixMap(theDrawingPort), &myRect, &myRect, srcCopy, NULL);
                
                HSetState(myData->imageData, mySavedState);
            }
            break;
            
        default:
            myErr = -1;
    }
 
    return(myErr);
}   
 
 
//////////
//
// ICUtils_RecompressCompressedImageWithTransparency
//
// Given an ImageDescriptionHandle and a handle to some image data, generate new RLE compressed data with
// optional hit-testing and transparency.
//
//////////
 
OSErr ICUtils_RecompressCompressedImageWithTransparency (
                                        ImageDescriptionHandle theOrigDesc,
                                        Handle theOrigImageData,
                                        RGBColor *theKeyColor, 
                                        RgnHandle theHitTestRegion,
                                        ImageDescriptionHandle *theImageDesc,
                                        Handle *theImageData)
{
    ICUtils_CompressedImageCompressProcData     myParams;
    OSErr                                       myErr = noErr;
    
    myParams.imageDesc = theOrigDesc;
    myParams.imageData = theOrigImageData;
    
    myErr = ICUtils_RecompressWithTransparencyFromProc(
                                        ICUtils_ImageCompressDrawProc,
                                        &myParams, 
                                        (Boolean)(theHitTestRegion != NULL), 
                                        theKeyColor, 
                                        theHitTestRegion, 
                                        theImageDesc,
                                        theImageData);
    
    return(myErr);
}
 
 
//////////
//
// ICUtils_RecompressPictureWithTransparency
//
// Given a QuickDraw PicHandle, generate new RLE compressed data with optional hit-testing and transparency.
//
//////////
 
OSErr ICUtils_RecompressPictureWithTransparency (
                                        PicHandle theOrigPicture,
                                        RGBColor *theKeyColor, 
                                        RgnHandle theHitTestRegion,
                                        ImageDescriptionHandle *theImageDesc,
                                        Handle *theImageData)
{
    ICUtils_PictureCompressProcData     myParams;
    OSErr                               myErr = noErr;
    
    myParams.picture = theOrigPicture;
    
    myErr = ICUtils_RecompressWithTransparencyFromProc(
                                        ICUtils_PictureCompressDrawProc,
                                        &myParams, 
                                        (Boolean)(theHitTestRegion != NULL), 
                                        theKeyColor, 
                                        theHitTestRegion, 
                                        theImageDesc,
                                        theImageData);
    return(myErr);
}
 
 
//////////
//
// ICUtils_RecompressPictureFileWithTransparency
//
// Given a QuickDraw PICT file, generate new RLE compressed data with optional hit -testing and transparency.
// This function uses ICUtils_RecompressPictureWithTransparency to do the actual work on the PicHandle retrieved from
// the PICT file.
//
//////////
 
OSErr ICUtils_RecompressPictureFileWithTransparency (
                                        FSSpec *theFSSpec, 
                                        RGBColor *theKeyColor, 
                                        RgnHandle theHitTestRegion,
                                        ImageDescriptionHandle *theImageDesc,
                                        Handle *theImageData)
{
    short       myRefNum = 0;
    PicHandle   myPicture = NULL;
    long        myEOF;
    long        myCount;
    OSErr       myErr = noErr;
    
    *theImageDesc = NULL;
    *theImageData = NULL;   
    
    myErr = FSpOpenDF(theFSSpec, fsRdPerm, &myRefNum);
    if (myErr != noErr)
        goto bail;
    
    myErr = GetEOF(myRefNum, &myEOF);
    if (myErr != noErr)
        goto bail;
    myEOF -= 512;
    
    myErr = SetFPos(myRefNum, fsFromStart, 512);
    if (myErr != noErr)
        goto bail;
            
    myPicture = (PicHandle)NewHandle(myEOF);
    myErr = MemError();
    if (myErr != noErr)
        goto bail;
 
    myCount = myEOF;
    HLock((Handle)myPicture);
    
    myErr = FSRead(myRefNum, &myCount, *myPicture);
    if (myErr != noErr)
        goto bail;
    
    HUnlock((Handle)myPicture);
    
    myErr = ICUtils_RecompressPictureWithTransparency(myPicture, theKeyColor, theHitTestRegion, theImageDesc, theImageData);
                                
bail:
    if (myPicture != NULL)
        DisposeHandle((Handle)myPicture);
        
    if (myRefNum != 0)
        FSClose(myRefNum);
        
    return(myErr);
}