DrawTextComponent.c

/*
    File:       DrawTextComponent.c
 
    Contains:   
 
    Written by: Mark Krueger    
 
    Copyright:  Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/29/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
#include <Memory.h>
#include <Resources.h>
#include <QuickDraw.h>  
#include <QDOffscreen.h>
#include <OSUtils.h>
#include <Errors.h>
#include <FixMath.h>
#include <ImageCodec.h>
 
#include    "DrawTextCodec.h"
InitCharTab(SharedGlobals *sGlob,ComponentInstance self);
extern long
Decompress(char *baseAddr,short rowBytes,short bwidth,short bheight,
    char *dataPtr);
 
extern long
Compress(char *baseAddr,short rowBytes,char *pBaseAddr,short pRowBytes,
    short width,short height,long spatialQ,long temporalQ,char *dataPtr,
    Fixed *similarityP,SharedGlobals *glob);
    
 
 
 
/*
    Our data structure declarations
*/
 
 
 
 
 
 
/* Function prototypes to keep the compiler smiling. */
 
pascal ComponentResult
DRAWTEXTCODEC(ComponentParameters *params,char **storage);
 
pascal ComponentResult
OpenCodec(ComponentInstance self);
 
pascal ComponentResult
CloseCodec(Handle storage,ComponentInstance self);
 
pascal ComponentResult
CanDoSelector(short selector);
 
pascal ComponentResult 
GetVersion();
 
pascal void
CompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
 
pascal void
DecompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
 
pascal long
CDPreCompress(Handle storage,register CodecCompressParams *p);
 
pascal long
CDBandCompress(Handle storage,register CodecCompressParams *p);
 
pascal long
CDPreDecompress(Handle storage,register CodecDecompressParams *p);
 
pascal long
CDBandDecompress(Handle storage,register CodecDecompressParams *p);
 
pascal ComponentResult
CDGetCodecInfo(Handle storage,CodecInfo *info);
pascal ComponentResult
CDGetCompressedImageSize(Handle storage,ImageDescriptionHandle desc,Ptr data,long dataSize,
    ICMDataProcRecordPtr dataProc,long *size);
 
pascal ComponentResult
CDGetMaxCompressionSize(Handle storage,PixMapHandle src,const Rect *srcRect,short depth,
    CodecQ quality,long *size);
 
pascal ComponentResult
CDGetCompressionTime(Handle storage,PixMapHandle src,const Rect *srcRect,short depth,
        CodecQ *spatialQuality,CodecQ *temporalQuality,unsigned long *time);
        
pascal ComponentResult
CDGetSimilarity(Handle storage,PixMapHandle src,const Rect *srcRect,ImageDescriptionHandle desc,
                Ptr data,Fixed *similarity);
                
pascal ComponentResult
CDTrimImage(Handle storage,ImageDescriptionHandle desc,Ptr inData,long inDataSize,
        ICMDataProcRecordPtr dataProc,Ptr outData,long outDataSize,ICMFlushProcRecordPtr flushProc,
        Rect *trimRect,ICMProgressProcRecordPtr progressProc);
ComponentResult
InitSharedTables(Globals **glob,ComponentInstance self);
/************************************************************************************ 
 *  This is the main dispatcher for our codec. All calls from the codec manager
 *  will come through here, with a unique selector and corresponding parameter block.
 *
 *  This routine must be first in the code segment of the codec thing.
 */
ProcInfoType __procinfo=kPascalStackBased
         | RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult)))
         | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ComponentParameters*)))
         | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char**)));
         
pascal ComponentResult
DRAWTEXTCODEC(ComponentParameters *params,char **storage)
{
    /*  If the selector is less than zero, it's a Component manager selector.   */
    
    if ( params->what < 0  ) { 
        switch ( params->what ) {
        case kComponentOpenSelect:
            return CallComponentFunction(params, (ComponentFunctionUPP)OpenCodec );
 
        case    kComponentCloseSelect:
            return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP) CloseCodec );
            
        case    kComponentCanDoSelect:
            return CallComponentFunction(params, (ComponentFunctionUPP) CanDoSelector);
 
        case kComponentVersionSelect : 
            return CallComponentFunction(params, (ComponentFunctionUPP) GetVersion);
 
        default :
            return (paramErr);
        }
    }
    
    /*
     *  Here we dispatch the rest of our calls. We use the magic thing manager routine which
     *  calls our subroutines with the proper parameters. The prototypes are in Image Codec.h.
     */
    
    switch ( params->what ) {
    case kImageCodecPreCompressSelect:  
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDPreCompress);
        
    case kImageCodecBandCompressSelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDBandCompress);
        
    case kImageCodecPreDecompressSelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDPreDecompress);
 
    case kImageCodecBandDecompressSelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDBandDecompress);
 
    case kImageCodecBusySelect:
        return 0;                   /* our codec is never asynchronously busy */
 
    case kImageCodecGetCodecInfoSelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCodecInfo);
        
    case kImageCodecGetCompressedImageSizeSelect    :
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCompressedImageSize);
 
    case kImageCodecGetMaxCompressionSizeSelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetMaxCompressionSize);
 
    case kImageCodecGetCompressionTimeSelect    :
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCompressionTime);
 
    case kImageCodecGetSimilaritySelect:
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetSimilarity);
 
    case kImageCodecTrimImageSelect :
        return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDTrimImage);
    
    default:
        return(paramErr);
    }   
}
 
 
/************************************************************************************ 
 *  This gets called when the thing instance is opened. We allocate our shared storage at this
 *  point. 
 
 *  If we have shared globals, we check if they exist, otherwise we allocate
 *  them and set the ComponentRefCon so that other instances can use them.
 *
 *  The shared globals hold our CodecInfo struct, which we read from our resource file,
 *  and some tables that we use for speed. If we cant get the tables we can work without
 *  them. All items in the shared globals are made purgeable when the last of our 
 *  instances is closed. If our component was loaded in the application heap ( because
 *  there was no room in the system heap) then we keep our shared storage in the app heap.
 *
 *  We keep a pointer to the shared globals in our instance globals so that other calls can get to them.
 */
 
 
ComponentResult
InitSharedTables(Globals **glob,ComponentInstance self)
{
    SharedGlobals   *sGlob;
    short           resFile;
    THz             saveZone;
    Boolean         inAppHeap;
    OSErr           result = noErr;
        
     
    saveZone = GetZone();
    inAppHeap = ( GetComponentInstanceA5(self) != 0 );
    if ( !inAppHeap )
        SetZone(SystemZone());
    if ( (sGlob=(SharedGlobals*)GetComponentRefcon((Component)self)) == nil  ) {
        if ( (sGlob = (SharedGlobals*)NewPtrClear(sizeof(SharedGlobals))) == nil ) { 
            result = MemError();
            goto obail;
        } 
        SetComponentRefcon((Component)self,(long)sGlob);
    }
 
    (*glob)->sharedGlob = sGlob;    // keep this around where it's easy to get at
    
 
    if ( sGlob->info == nil || *(Handle)sGlob->info == nil  )  {
        if ( sGlob->info ) 
            DisposeHandle((Handle)sGlob->info);
 
        /* Get the CodecInfo struct which we keep in our resource fork */
        
        resFile = OpenComponentResFile((Component)self);
#ifndef THINK_C     // if running linked this will return an error since the res file is already open
        if ( (result=ResError() ) != 0 ) 
            goto obail;
#endif  THINK_C
        sGlob->info = (CodecInfo **) GetResource(codecInfoResourceType,128);
        if ( sGlob->info == nil ) {
            CloseComponentResFile(resFile);
            result = ResError();
            goto obail;
        }
        LoadResource((Handle)sGlob->info);
        if ( ResError() ) {
            sGlob->info = nil;
            CloseComponentResFile(resFile);
            result = ResError();
            goto obail;
        }
        DetachResource((Handle)sGlob->info);
        CloseComponentResFile(resFile);
    }
    HNoPurge((Handle)sGlob->info);
    
obail:
 
    SetZone(saveZone);
    if ( result != noErr && sGlob != nil ) {
        if ( sGlob->info )
            DisposeHandle((Handle)sGlob->info);
        DisposePtr((Ptr)sGlob);
        SetComponentRefcon((Component)self,(long)nil);
    }
    return(result);
}
 
/************************************************************************************ 
 *  This gets called when the thing instance is opened. We allocate our storage at this
 *  point. If we have shared globals, we check if they exist, and put a pointer to them 
 *  in our instance globals so that other calls can get to them.
 */
 
pascal ComponentResult
OpenCodec(ComponentInstance self)
{
    ComponentResult result;
    Globals         **glob;
    
    /* 
        First we allocate shared storage. This should be a handle to any
        kind of data used by the thing instance. They should be allocated
        in the current heap.
    
    */   
         
    if ( (glob = (Globals **)NewHandleClear(sizeof(Globals))) == nil )  {
        return(MemError());
    }
    SetComponentInstanceStorage(self,(Handle)glob);
    
    /*  Check and initialize our shared globals */
 
    (*glob)->self = self;
    result = InitSharedTables(glob,self);
    
    
    return result;
}
 
 
/************************************************************************************ 
 *  This gets called when the thing instance is closed. We need to get rid of any 
 *  instance storage here. 
 */
 
pascal ComponentResult
CloseCodec(Handle storage,ComponentInstance self)
{
    SharedGlobals   *sGlob;
    Globals         **glob = (Globals **)storage;
        
    /*  If we are closing our last instance 
        then we make the shared global items purgeable to
        speed things up next instance.
     */
    
    if ( CountComponentInstances((Component)self) == 1) {
        if ( (sGlob=(SharedGlobals*)(*glob)->sharedGlob) != nil  ) {
            if ( sGlob->info )
                HPurge((Handle)sGlob->info);
            if ( sGlob->tableWorld ) {
                DisposeGWorld(sGlob->tableWorld );
                sGlob->tableWorld = nil;
            }
        }
    }
    if ( glob )  {
        DisposeHandle((Handle)glob);
    }
    return(noErr);
}
 
 
 
/************************************************************************************ 
 *  Return true if we can handle the selector, otherwise false.
 */
 
pascal ComponentResult
CanDoSelector(short selector)
{   
    switch(selector) {
        case kComponentOpenSelect:
        case kComponentCloseSelect:
        case kComponentCanDoSelect:
        case kComponentVersionSelect : 
        case kImageCodecPreCompressSelect:
        case kImageCodecBandCompressSelect:
        case kImageCodecPreDecompressSelect:
        case kImageCodecBandDecompressSelect:
        case kImageCodecBusySelect:
        case kImageCodecGetCodecInfoSelect:
        case kImageCodecGetCompressedImageSizeSelect:
        case kImageCodecGetMaxCompressionSizeSelect:
        case kImageCodecGetCompressionTimeSelect:
        case kImageCodecGetSimilaritySelect:
        case kImageCodecTrimImageSelect:
            return(true);
        default:
            return (false);
    }
}
 
 
/************************************************************************************ 
 *  Return the version of this component ( defines interface ) and revision level
 *  of the code.
 */
 
pascal ComponentResult 
GetVersion()
{
    return ((1<<16) | 1);       /* interface version in hi word, code rev in lo word  */
}
 
 
/************************************************************************************ 
 *  CDPreCompress gets called before an image is compressed, or whenever the source pixmap
 *  pixel size changes when compressing a sequence. We return information about
 *  how we can compress the image to the codec manager, so that it can fit the source data
 *  to our requirements. The ImageDescriptor is already filled in, so we can use it for 
 *  reference (or even add to it ). PixelSize is the pixel depth of the source pixmap, we
 *  use this as a reference for deciding what we can do. The other parameters return information
 *  to the CodecManager about what we can do. We can also do setup here if we want to.
 */
 
pascal long
CDPreCompress(Handle storage,register CodecCompressParams *p)
{
    Globals             **glob  = (Globals **)storage;
    CodecCapabilities *capabilities = p->capabilities;
 
    /*
     *  First we return which depth input pixels we can deal with - based on what the
     *  app has available - we can only work with 32 bit input pixels.
     */
       
    switch ( (*p->imageDescription)->depth )  {
        case 1:
            capabilities->wantedPixelSize = 1;
            break;
        default:
            return(codecConditionErr);
            break;
    }
 
    /* if the buffer gets banded,  return the smallest one we can deal with */
    
    capabilities->bandMin = FONT_HEIGHT;
 
    /* if the buffer gets banded, return the increment it be should grown */
 
    capabilities->bandInc = FONT_HEIGHT;
    
    
    /*
     *  If a codec needs the dimensions of the source pixmap to be of certain multiples
     *  it can ask for the image to be extended out (via pixel replication) vertically
     *  and/or horizontally.
     */
 
    if ( (*p->imageDescription)->width % FONT_WIDTH )
        capabilities->extendWidth = FONT_WIDTH & (FONT_WIDTH - ((*p->imageDescription)->width % FONT_WIDTH));
    if ( (*p->imageDescription)->height % FONT_HEIGHT )
        capabilities->extendHeight = FONT_HEIGHT & (FONT_HEIGHT - ((*p->imageDescription)->height % FONT_HEIGHT));
    
    if ( (*glob)->sharedGlob->tableWorld == nil )   
        InitCharTab((*glob)->sharedGlob,(*glob)->self);
 
    return(noErr);
}
 
 
/************************************************************************************ 
 *  CDBandCompress gets called when the codec manager wants us to compress an image, or a horizontal 
 *  band of an image. The pixel data at sBaseAddr is guaranteed to conform to the criteria we 
 *  demanded in BeginCompress.
 */
 
pascal long
CDBandCompress(Handle storage,register CodecCompressParams *p)
{
    short               width,height;
    Ptr                 cDataPtr,dataStart;
    short               depth;
    Rect                sRect;
    long                offsetH,offsetV;
    Globals             **glob  = (Globals **)storage;
    register char       *baseAddr;
    long                numLines,numStrips;
    short               rowBytes;
    char                mmuMode = 1;
    register short      y;
    ImageDescription    **desc = p->imageDescription;
    OSErr               result = noErr;
    Fixed               simil;
    long                csize;
    CodecQ              spatialQ,temporalQ;
    
    
    
    /*  If there is a progress proc, give it an open call at the start of this band. */
 
    if (p->progressProcRecord.progressProc)
        CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressOpen,0,
            p->progressProcRecord.progressRefCon);
 
    width = (*desc)->width;
    height = (*desc)->height;
    depth = (*desc)->depth;
    dataStart = cDataPtr = p->data;
    spatialQ = p->spatialQuality;
    temporalQ = p->temporalQuality;
    
    /* figure out offset to first pixel in baseAddr from the pixelsize and bounds */
 
    rowBytes = p->srcPixMap.rowBytes & 0x3fff;
    sRect =  p->srcPixMap.bounds;
    numLines = p->stopLine - p->startLine;          /* number of scanlines in this band */
    numStrips = numLines/FONT_HEIGHT;               /* number of strips in this band */
    
    /* adjust the source baseAddress to be at the beginning of the desired rect */
 
    switch ( p->srcPixMap.pixelSize ) {
    case 1:
        offsetH = sRect.left>>3;                    /* 8 pixels = 1 byte */
        break;
    default:
        result = codecErr;
        goto bail;
    }
    offsetV = sRect.top * rowBytes;
    baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV;
 
 
    if ( p->conditionFlags & codecConditionFirstBand) {
        (*glob)->totalSize = 0;
    }
    else { 
        /* if there is not a flush proc, adjust the pointer to the next band */
        
        if (  p->flushProcRecord.flushProc == nil )
            cDataPtr += (*glob)->totalSize;
    }
 
    /* do the slower flush/progress case if we have too */
    
    if (  p->flushProcRecord.flushProc  || p->progressProcRecord.progressProc ) {
        SharedGlobals *sg = (*glob)->sharedGlob;
 
 
        for ( y=0; y < numStrips; y++) {
            SwapMMUMode(&mmuMode);
            csize = Compress(baseAddr,rowBytes,nil,0,width/FONT_WIDTH,1,spatialQ,temporalQ,
                    cDataPtr,&simil,sg);
            SwapMMUMode(&mmuMode);
            baseAddr += rowBytes*FONT_HEIGHT;
            if ( p->flushProcRecord.flushProc ) { 
                if ( (result=CallICMFlushProc(p->flushProcRecord.flushProc,cDataPtr,csize,
                        p->flushProcRecord.flushRefCon)) != noErr) {
                    result = codecSpoolErr;
                    goto bail;
                }
            } else {
                cDataPtr += csize;
            }
            (*glob)->totalSize += csize;
            if (p->progressProcRecord.progressProc) {
                if ( (result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent,
                    FixDiv(y,numStrips),p->progressProcRecord.progressRefCon)) != noErr ) {
                        result = codecAbortErr;
                        goto bail;
                    }
            }
        }
    } else 
    {
        SharedGlobals *sg = (*glob)->sharedGlob;
 
        SwapMMUMode(&mmuMode);
        csize = Compress(baseAddr,rowBytes,nil,0,width/FONT_WIDTH,numStrips,spatialQ,
            temporalQ,cDataPtr,&simil,sg);
        SwapMMUMode(&mmuMode);
        cDataPtr += csize;
        (*glob)->totalSize += csize;
    }
 
    /*
    
        return size and similarity on the last band 
        
    */
    
    if ( p->conditionFlags & codecConditionLastBand ) {
        (*p->imageDescription)->dataSize = (*glob)->totalSize;  /* return the actual size of the compressed data */
        p->similarity = 0;                                      /* we don't do frame differencing */
    }
    
bail:
    /*  If there is a progress proc, give it a close call at the end of this band. */
 
    if (p->progressProcRecord.progressProc)
        CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0,
            p->progressProcRecord.progressRefCon);
        
    return(result);
}
 
 
 
/************************************************************************************ 
 *  CDPreDecompress gets called before an image is decompressed. We return information about
 *  how we can decompress the image to the codec manager, so that it can fit the destination data
 *  to our requirements. 
 */
 
pascal long
CDPreDecompress(Handle storage,register CodecDecompressParams *p)
{
    #pragma unused(storage)
    register CodecCapabilities  *capabilities = p->capabilities;
    Rect    dRect = p->srcRect;
    
    /*  Check if the matrix is okay for us. We don't do anything fancy. */
    
    if ( !TransformRect(p->matrix,&dRect,nil) )
        return(codecConditionErr);
 
 
    /*  Decide which depth compressed data we can deal with. */
    
    switch ( (*p->imageDescription)->depth )  {
        case 1:
            break;
        default:
            return(codecConditionErr);
            break;
    }
    
    /*  We can deal only 1 bit pixels. */
 
    capabilities->wantedPixelSize = 1;  
        
    capabilities->bandMin = FONT_HEIGHT;
 
    capabilities->bandInc = FONT_HEIGHT;
    
    /*  If we needed our pixels to be aligned on some integer multiple we would set these to
     *  the number of pixels we need the dest extended by. If we dont care, or we take care of
     *  it ourselves we set them to zero.
     */
 
    if ( (*p->imageDescription)->width % FONT_WIDTH )
        capabilities->extendWidth = FONT_WIDTH & (FONT_WIDTH - ((*p->imageDescription)->width % FONT_WIDTH));
    if ( (*p->imageDescription)->height % FONT_HEIGHT )
        capabilities->extendHeight = FONT_HEIGHT & (FONT_HEIGHT - ((*p->imageDescription)->height % FONT_HEIGHT));
    
    return(noErr);
}
 
 
/************************************************************************************ 
 *  CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal 
 *  band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we 
 *  demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and
 *  we need to clear bits in it that correspond to any pixels in the destination we do not want to 
 *  change. ( We always write all pixels, so we dont care. This mode is important only for those
 *  codecs that have frame differencing and don't always write all the pixels. )
 */
 
pascal long
CDBandDecompress(Handle storage,register CodecDecompressParams *p)
{
    Rect                dRect;
    long                offsetH,offsetV;
    Globals             **glob  = (Globals **)storage;
    long                numLines,numStrips;
    short               rowBytes;
    long                stripBytes;
    short               width;
    register short      y;
    register char       *baseAddr;
    char                *cDataPtr;
    char                mmuMode = 1;
    OSErr               result = noErr;
    long                csize;
    
    /*  Calculate the real base address based on the bounds rect. If it's not 
        a linear transformation, we dont do it. */
 
    dRect = p->srcRect;
    if ( !TransformRect(p->matrix,&dRect,nil) )
        return(paramErr);
 
    /*  If there is a progress proc, give it an open call at the start of this band. */
 
    if (p->progressProcRecord.progressProc)
        CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressOpen,0,
            p->progressProcRecord.progressRefCon);
    
    
    /* initialize some local variables */
    
    width = (*p->imageDescription)->width;
    rowBytes = p->dstPixMap.rowBytes;                   
    numLines = p->stopLine - p->startLine;              /* number of scanlines in this band */
    numStrips = numLines/FONT_HEIGHT;                   /* number of strips in this band */
    stripBytes = (width + (FONT_WIDTH-1))/FONT_WIDTH;   /* number of bytes in one strip of blocks */
    cDataPtr = p->data;
    
    /* adjust the destination baseaddress to be at the beginning of the desired rect */
    
    offsetH = (dRect.left - p->dstPixMap.bounds.left);
    switch (  p->dstPixMap.pixelSize ) {
    case 1:
        offsetH >>= 3;                  /* 8 pixel = 1 bytes */
        break;
    default:
        result = codecErr;              /* we dont handle these cases, thow we could */
        goto bail;
    }
    offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes;
    baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
 
 
    /* 
     *  If we are skipping some data, we just skip it here. We can tell because
     *  firstBandInFrame says this is the first band for a new frame, and
     *  if startLine is not zero, then that many lines were clipped out.
     */
 
    if ( (p->conditionFlags & codecConditionFirstBand) ) {
        cDataPtr += p->startLine * stripBytes;
    }
    
    /*
     *  If theres a dataproc spooling the data to us, then we have to do the data
     *  in whatever size chunks they want to give us, or if there is a progressProc
     *  make sure to call it as we go along.
     */
 
    
    if ( p->dataProcRecord.dataProc || p->progressProcRecord.progressProc ) {
        SharedGlobals *sg = (*glob)->sharedGlob;
    
        for (y=0; y < numStrips; y++) {
            if (p->dataProcRecord.dataProc) {
                if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes,
                        p->dataProcRecord.dataRefCon)) != noErr ) {
                    result = codecSpoolErr;
                    goto bail;
                }
            }
            SwapMMUMode(&mmuMode);
            csize = Decompress(baseAddr,rowBytes,width/FONT_WIDTH,1,cDataPtr);
            SwapMMUMode(&mmuMode);
            baseAddr += rowBytes*FONT_HEIGHT;
            cDataPtr += csize;
            if (p->progressProcRecord.progressProc) {
                if ( (result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent,
                    FixDiv(y, numStrips),p->progressProcRecord.progressRefCon)) != noErr ) {
                    result = codecAbortErr;
                     goto bail;
                }
            }
        }
    
    
    /* 
     *
     * otherwise - do the fast case. 
     *
     */
         
    } else
     {
        
        SwapMMUMode(&mmuMode);
        csize = Decompress(baseAddr,rowBytes,width/FONT_WIDTH,numStrips,cDataPtr);
        SwapMMUMode(&mmuMode);
        cDataPtr += csize;
    }
    
    /* 
     *
     *  IMPORTANT: update pointer to data in params, so when we get the  next
     *  band we'll be at the right place in our data.
     *  
     */
    
    p->data = cDataPtr;
    
    if ( p->conditionFlags & codecConditionLastBand ) {
        /* Tie up any loose ends on the last band of the frame, if we had any */
    }
 
bail:
    /*  If there is a progress proc, give it a close call at the end of this band. */
 
    if (p->progressProcRecord.progressProc)
        CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0,
            p->progressProcRecord.progressRefCon);
    return(result);
}
 
 
/************************************************************************************ 
 *  CDGetCodecInfo allows us to return information about ourselves to the codec manager.
 *  
 *  There will be a tool for determining appropriate values for the accuracy, speed
 *  and level information. For now we estimate with scientific wild guessing.
 *
 *  The info is stored as a resource in the same file with our component.
 */
 
pascal ComponentResult
CDGetCodecInfo(Handle storage,CodecInfo *info)
{
    Globals **glob = (Globals **)storage;
 
    if ( info == nil ) 
        return(paramErr);
    BlockMove((Ptr)*((*glob)->sharedGlob)->info,(Ptr)info,sizeof(CodecInfo));
    return(noErr);
}
 
 
/************************************************************************************ 
 *  When CDGetSimilarity is called, we return the percent of the compressed image A that
 *  is different from compressed image B. This can be used by applications that use sequence
 *  dynamics as an indicator for editing image sequences.
 *  
 *  If the codec cannot do a direct similarity comparison, the ICM decompresses image A and
 *  do a comparison with image B.  This call is provided so that a codec can save the time
 *  of the intermediate decompress if it can do the comparison directly.
 */
 
pascal ComponentResult
CDGetSimilarity(Handle storage,PixMapHandle src,const Rect *srcRect,ImageDescriptionHandle desc,
                Ptr data,Fixed *similarity)
{
#pragma unused(storage,src,srcRect,desc,data,similarity)
 
    /*  This call is not implemented yet, which is okay, because its not used very much and is not mandatory */
 
    return(codecUnimpErr);
}
 
 
/************************************************************************************ 
 *  When CDGetCompressedImageSize is called, we return the size in bytes of the given compressed
 *  data ( for one image frame).
 */
 
pascal ComponentResult
CDGetCompressedImageSize(Handle storage,ImageDescriptionHandle desc,Ptr data,long dataSize,
    ICMDataProcRecordPtr dataProc,long *size)
{
#pragma unused(storage,data,dataSize,dataProc)
    
        
    short width = (*desc)->width;
    short height = (*desc)->height;
    if ( size == nil )
        return(paramErr);
 
    /*  we always end up with a fixed size. If we did not, we would return the worst case size */
    
    *size = ((width + (FONT_WIDTH-1))/FONT_WIDTH) * ((height+(FONT_HEIGHT-1))/FONT_HEIGHT); 
    return(noErr);
}
 
 
/************************************************************************************ 
 *  When CDGetMaxCompressionSize is called, we return the maximum size the compressed data for
 *  the given image would be in bytes.
 */
 
pascal ComponentResult
CDGetMaxCompressionSize(Handle storage,PixMapHandle src,const Rect *srcRect,short depth,
    CodecQ quality,long *size)
{
#pragma unused(storage,src,depth,quality)
    
    short width = srcRect->right - srcRect->left;
    short height = srcRect->bottom - srcRect->top;
 
    /*  we always end up with a fixed size. If we did not, we would return the worst case size */
    
    *size = 100 + ((width + (FONT_WIDTH-1))/FONT_WIDTH) * ((height+(FONT_HEIGHT-1))/FONT_HEIGHT);   
 
    return(noErr);
}
 
 
/************************************************************************************ 
 *  When CDGetCompressionTime is called, we return the approximate time for compressing
 *  the given image would be in milliseconds. We also return the closest actual quality
 *  we can handle for the requested value.
 */
 
pascal ComponentResult
CDGetCompressionTime(Handle storage,PixMapHandle src,const Rect *srcRect,short depth,
        CodecQ *spatialQuality,CodecQ *temporalQuality,unsigned long *time)
{
#pragma unused(storage,src,srcRect,depth)
 
    if (time)
        *time = 0;                                  /* we don't know how many msecs */
 
    if ( spatialQuality ) {
        if ( *spatialQuality <= codecLowQuality ) {
            *spatialQuality = codecLowQuality;
        }
        else if ( *spatialQuality <= codecNormalQuality ) {
            *spatialQuality = codecNormalQuality;
        } else {
            *spatialQuality = codecHighQuality;
        }
    }
    if ( temporalQuality ) 
        *temporalQuality = 0;
    return(noErr);
}
 
 
/************************************************************************************ 
 *  When CDTrimImage is called, we take the given compressed data and return only the portion
 *  which is represented by the trimRect. We can return a little more if we have too, but we
 *  need only return enough so that the image in trimRect is properly displayed. We then
 *  adjust the rectangle to corresond to the same rectangle in the new trimmed data.
 */
 
pascal ComponentResult
CDTrimImage(Handle storage,ImageDescriptionHandle desc,Ptr inData,long inDataSize,
        ICMDataProcRecordPtr dataProc,Ptr outData,long outDataSize,ICMFlushProcRecordPtr flushProc,
        Rect *trimRect,ICMProgressProcRecordPtr progressProc)
{
    #pragma unused(storage,desc,inData,inDataSize,dataProc,outData,outDataSize,flushProc,trimRect,progressProc)
    return(codecUnimpErr);
 
}