home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / QuickTime / Sample Code / QT Codec Acceleration / Samples / ExampleCodec / examplecodec.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-26  |  48.5 KB  |  1,766 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        examplecodec.c
  3.  
  4.     Copyright:    © 1991-1996 by Apple Computer, Inc., all rights reserved.
  5.  
  6.     This is an example of am image compression codec that handles both
  7.     compression and decompression of images as passed to it by the 
  8.     Image Compression manager. It is built as a Component Manager Component.
  9.  
  10.     The compression scheme here is 420 YUV. The image is stored as separate 
  11.     luminance and chrominance channels. For each 2x2 block of pixels in the
  12.     source image we store 4 luminance (Y) components, 1 Y-Red component (U) and
  13.     1 Y-Blue (V) component. Each Y-component is stored as 6-bits,  resulting in a 
  14.     savings of 2.4:1 over a 24-bit/pixel image (6*4 + 2*8)/4 = 10 bits/pixel.
  15.  
  16. */
  17.  
  18. #include <Memory.h>
  19. #include <Resources.h>
  20. #include <Quickdraw.h>    
  21. #include <QDOffscreen.h>
  22. #include <OSUtils.h>
  23. #include <Errors.h>
  24. #include <FixMath.h>
  25.  
  26. #include "ImageCodec.h"
  27.  
  28. #ifndef _CreateRoutineDescriptor_
  29. #define _CreateRoutineDescriptor_
  30. #if GENERATINGPOWERPC
  31.     #define ExternRoutineDescriptor(info, proc)                                    \
  32.      extern RoutineDescriptor g##proc##RD;
  33.     
  34.     #define CreateRoutineDescriptor(info, proc)                                    \
  35.      RoutineDescriptor g##proc##RD = BUILD_ROUTINE_DESCRIPTOR(info, proc);
  36.     
  37.     #define GetRoutineAddress(proc)    (&g##proc##RD)
  38. #else
  39.     #define ExternRoutineDescriptor(info, proc)
  40.     #define CreateRoutineDescriptor(info, proc)
  41.  
  42.     #define GetRoutineAddress(proc)    (proc)
  43. #endif
  44. #endif
  45.  
  46.  
  47. #ifdef    POWERPC_NATIVE
  48.     #pragma    options align=mac68k
  49. #endif
  50.  
  51. #ifndef NOASM
  52.     #define NOASM 0
  53. #endif
  54.  
  55. #ifndef DECO_BUILD
  56.     #define DECO_BUILD 0
  57. #endif
  58.  
  59. #ifndef COMP_BUILD
  60.     #define COMP_BUILD 0
  61. #endif
  62.  
  63. #if !DECO_BUILD && !COMP_BUILD
  64.     #error "must specify either DECO_BUILD or COMP_BUILD"
  65. #endif
  66.  
  67. #if DECO_BUILD && !defined(ASYNC_DECODE)
  68.     #define ASYNC_DECODE 1            // if defined, use scheduled asynchronous display
  69. #endif
  70.  
  71. #ifndef ASYNC_DECODE
  72.     #define ASYNC_DECODE 0
  73. #endif
  74.  
  75. #if !defined(QT_MP) && ASYNC_DECODE && defined(POWERPC_NATIVE)
  76.     #define QT_MP 1
  77. #endif
  78.  
  79. #ifndef QT_MP
  80.     #define QT_MP 0
  81. #endif
  82.  
  83. #if QT_MP && !ASYNC_DECODE
  84.     #error "can't QT_MP without ASYNC_DECODE"
  85. #endif
  86.  
  87. /* Version information */
  88.  
  89. #define    EXAMPLE_CODEC_REV            2
  90. #define    codecInterfaceVersion        2                /* high word returned in component GetVersion */
  91.  
  92.  
  93.  
  94. /* Some useful macros and constants */
  95.  
  96. #define    R_W    0x4ccd
  97. #define    G_W    0x970a
  98. #define    B_W    0x1c29
  99. #define    PIN(_n)        ((_n) < 0 ? 0 : (_n) > 255 ? 255 : (_n))
  100.  
  101.  
  102. /*
  103.     Our data structure declarations
  104. */
  105.  
  106. #if DECO_BUILD
  107.  
  108. #define QUEUE_SIZE 30
  109.  
  110. #ifndef fieldOffset
  111.     #define fieldOffset(type, field) ((short) &((type *) 0)->field)
  112. #endif
  113.  
  114. /*
  115.     The DecompressRecord is used to store the information needed
  116.     to decompress a frame asynchronously.
  117. */
  118. struct DecompressRecord {
  119.     ComponentMPWorkFunctionHeaderRecord    header;
  120.     void *nextBusy;            // next DecompressRecord that is queued up
  121.     void *nextFree;            // next unused DecompressRecord
  122.     Ptr srcData;            // pointer to compressed data
  123.     Ptr baseAddr;            // base address of destination PixMap
  124.     short rowBytes;            // rowBytes parameter of dest PixMap
  125.     short width;            // width (in pixels) of a row
  126.     short numStrips;        // number of strips to draw
  127.     long srcDataIncrement;    // increment for srcData between strips
  128.     long baseAddrIncrement;    // increment for baseAddr between strips
  129.     Boolean shieldCursor;    // if we need to shield the cursor
  130.     Boolean inQueue;
  131.     ICMCompletionProcRecord completionProc;    // completion proc record to call when done
  132.     TimeValue frameTime;    // what time to decompress this frame
  133.     Fixed rate;                // rate of movie
  134.     long scale;                // time scale
  135.     struct Globals *glob;    // pointer to our globals
  136. };
  137. typedef struct DecompressRecord DecompressRecord;
  138.  
  139. #endif
  140.  
  141. /* This is the structure we use to hold data used by all instances of
  142.    this compressor and decompressor */
  143.  
  144. typedef struct    {                    
  145.     Handle    rgbwTable;                    /* optional encode/decode table */
  146.     Handle    giwTable;                    /* another optional encode/decode table */
  147.     CodecInfo    **info;                    /* our cached codec info structure */
  148. } SharedGlobals;
  149.  
  150.  
  151. /* This is the structure we use to store our global data for each instance */
  152.  
  153. typedef struct    {                        
  154.     SharedGlobals    *sharedGlob;        /* pointer to instance-shared globals */
  155. #if ASYNC_DECODE
  156.     QTCallBack            callBack;                // our call back
  157.     QTCallBackUPP        decompressCallBackUPP;    // pointer to our decompress callback
  158.     ImageSequence        sequenceID;
  159.     QHdr                busyQueueHead;            // queue header for queued decompress records
  160.     QHdr                freeQueueHead;            // queue header for free decompress records
  161.     DecompressRecord    queue[QUEUE_SIZE];        // for queued frames
  162.     long                a5World;                // A5 world for decompress callback
  163.     Boolean                queueIsRunning;            // set if decompress queue is currently running
  164.     ComponentInstance    target;
  165.     volatile short        decompressCount;        // keep track of frames being decompressed/compressed (should only ever hit 1)
  166.  
  167.     ComponentMPWorkFunctionUPP
  168.                         decompressFunction;
  169.     void                *decompressFunctionRefCon;
  170. #endif
  171. } Globals;
  172.  
  173.  
  174. #define IMAGECODEC_BASENAME() CD
  175. #define IMAGECODEC_GLOBALS() Globals *storage
  176.  
  177. #define CALLCOMPONENT_BASENAME IMAGECODEC_BASENAME
  178. #define CALLCOMPONENT_GLOBALS IMAGECODEC_GLOBALS
  179.  
  180. #include "ImageCodec.k.h"
  181. #include "Components.k.h"
  182.  
  183.  
  184.  
  185. /* Function prototypes to keep the compiler smiling. */
  186.  
  187. #if QT_MP
  188.     Boolean TestBusy( Globals *glob );
  189. #endif
  190.  
  191. #if COMP_BUILD
  192. pascal void
  193. CompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
  194. #endif
  195.  
  196. #if DECO_BUILD
  197. pascal void
  198. DecompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg);
  199. #endif
  200.  
  201. ComponentResult
  202. InitSharedTables(Globals *glob,ComponentInstance self);
  203.  
  204. #if ASYNC_DECODE
  205. pascal ComponentResult DecompressWorkFunction(Globals *glob, DecompressRecord *drp);
  206.  
  207. CreateRoutineDescriptor(uppComponentMPWorkFunctionProcInfo, DecompressWorkFunction)
  208.  
  209. pascal void DecompressCallBack(QTCallBack cb,long refcon);
  210.     
  211. CreateRoutineDescriptor(uppQTCallBackProcInfo, DecompressCallBack)
  212. #endif
  213.  
  214.  
  215. /************************************************************************************ 
  216.  *    This is the main dispatcher for our codec. All calls from the codec manager
  217.  *    will come through here, with a unique selector and corresponding parameter block.
  218.  *
  219.  *    This routine must be first in the code segment of the codec component.
  220.  */
  221.  
  222. pascal ComponentResult CDComponentDispatch(ComponentParameters *params, Globals *storage);
  223.  
  224. #ifdef POWERPC_NATIVE
  225. struct RoutineDescriptor CDComponentDispatchRD =
  226.           BUILD_ROUTINE_DESCRIPTOR((kPascalStackBased | RESULT_SIZE (kFourByteCode) |
  227.                             STACK_ROUTINE_PARAMETER (1, kFourByteCode) |
  228.                             STACK_ROUTINE_PARAMETER (2, kFourByteCode)),CDComponentDispatch);
  229. #endif
  230.  
  231. static ProcPtr CDFindRoutineProcPtr(short selector, ProcInfoType *procInfo);
  232.  
  233. pascal ComponentResult CDComponentDispatch(ComponentParameters *params, Globals *storage)
  234. {
  235.     ProcPtr theProc;
  236.     ProcInfoType theProcInfo;
  237.     ComponentResult result = codecUnimpErr;
  238.  
  239.      theProc = CDFindRoutineProcPtr(params->what, &theProcInfo);
  240.     if (theProc) 
  241.         result = CallComponentFunctionWithStorageProcInfo((Handle)storage, params, theProc, theProcInfo);
  242.  
  243.     return result;
  244. }
  245.  
  246. static ProcPtr CDFindRoutineProcPtr(short selector, ProcInfoType *procInfo)
  247. {
  248.     ProcPtr aProc;
  249.     ProcInfoType pi;
  250.  
  251. #define ComponentCall(a)    case kComponent##a##Select:  aProc = (ProcPtr)CD##a; pi = uppCallComponent##a##ProcInfo; break;
  252. #define CodecCall(a)        case kImageCodec##a##Select: aProc = (ProcPtr)CD##a; pi = uppImageCodec##a##ProcInfo; break;
  253. #if COMP_BUILD
  254. #define CompressCall(a)        CodecCall(a)
  255. #define DecompressCall(a)
  256. #endif
  257. #if DECO_BUILD
  258. #define CompressCall(a)
  259. #define DecompressCall(a)    CodecCall(a)
  260. #endif
  261.  
  262. #define ComponentError(a)
  263. #define DecompressError(a)
  264. #define CompressError(a)
  265.  
  266.  
  267.     switch (selector) {
  268.         /* the multiprocessor stuff will explode if called from 68K ... so don't let the 
  269.             MP INIT think that this code supports MP */
  270.     #if ASYNC_DECODE && QT_MP
  271.         ComponentCall    (GetMPWorkFunction)
  272.     #else
  273.         ComponentError    (GetMPWorkFunction)
  274.     #endif
  275.         ComponentError    (Unregister)
  276.     #if ASYNC_DECODE && QT_MP
  277.         ComponentCall    (Target)
  278.     #else
  279.         ComponentError    (Target)
  280.     #endif
  281.         ComponentError    (Register)
  282.         ComponentCall    (Version)
  283.         ComponentCall    (CanDo)
  284.         ComponentCall    (Close)
  285.         ComponentCall    (Open)
  286.  
  287.         CodecCall        (GetCodecInfo)
  288.         
  289.         CompressCall    (GetCompressionTime)
  290.         CompressCall    (GetMaxCompressionSize)
  291.         CompressCall    (PreCompress)
  292.         CompressCall    (BandCompress)
  293.         DecompressCall    (PreDecompress)
  294.         
  295.         DecompressCall    (BandDecompress)
  296.         CodecCall        (Busy)
  297.         DecompressCall    (GetCompressedImageSize)
  298.         DecompressError    (GetSimilarity)
  299.         DecompressCall    (TrimImage)
  300.     
  301.         CompressError    (RequestSettings)
  302.         CompressError    (GetSettings)
  303.         CompressError    (SetSettings)
  304.     #if ASYNC_DECODE
  305.         DecompressCall    (Flush)
  306.     #endif
  307.         ComponentError    (SetTimeCode)
  308.     
  309.         DecompressError    (IsImageDescriptionEquivalent)
  310.         ComponentError    (NewMemory)
  311.         ComponentError    (DisposeMemory)
  312.         DecompressError    (HitTestData)
  313.         ComponentError    (NewImageBufferMemory)
  314.         
  315.         DecompressError    (ExtractAndCombineFields)
  316.         
  317.         default:
  318.             aProc = nil;
  319.             pi = 0;
  320.         }
  321.  
  322.     *procInfo = pi;
  323.     return aProc;
  324. }
  325.  
  326. /************************************************************************************ 
  327.  *     Return true if we can handle the selector, otherwise false.
  328.  */
  329.  
  330. pascal ComponentResult CDCanDo(Globals *storage, short selector) 
  331. {    
  332. #pragma unused(storage)
  333.     ProcInfoType ignoreResult;
  334.     
  335.     return (CDFindRoutineProcPtr(selector,&ignoreResult) != 0);
  336. }
  337.  
  338. /************************************************************************************ 
  339.  *    This gets called when the component instance is opened. We allocate our storage at this
  340.  *    point. If we have shared globals, we check if they exist, and put a pointer to them 
  341.  *    in our instance globals so that other calls can get to them.
  342.  */
  343.  
  344. pascal ComponentResult CDOpen(Globals *storage, ComponentInstance self)
  345. {
  346. #pragma unused(storage)
  347.     ComponentResult result;
  348.     Globals             *glob;
  349.     
  350.     /* 
  351.         First we allocate our local storage. This should store any
  352.         kind of data used by the component instance. It should be allocated
  353.         in the current heap.
  354.     */     
  355.          
  356.     if ( (glob = (Globals *)NewPtrClear(sizeof(Globals))) == nil )  {
  357.         return(MemError());
  358.     }
  359.     SetComponentInstanceStorage(self,(Handle)glob);
  360.     
  361.     /*     Check and initialize our shared globals */
  362.     
  363.     result = InitSharedTables(glob,self);
  364.  
  365. #if ASYNC_DECODE
  366.     /*
  367.         if we our doing an async decode, then make a routine descriptor for our
  368.         callback proc
  369.     */
  370.  
  371.     glob->decompressCallBackUPP = GetRoutineAddress(DecompressCallBack);
  372.  
  373.     // Start the world with every frame queued up on the free frame list 
  374.     // and nothing queued on the busy frame list.
  375.     {
  376.         short i;
  377.  
  378.         for(i=0; i<QUEUE_SIZE; i++){
  379.             DecompressRecord *drp = &glob->queue[i];
  380.             Enqueue((QElem *)&drp->nextFree, &glob->freeQueueHead);
  381.         }
  382.     }
  383.  
  384.         glob->target = self;
  385.  
  386. #endif
  387.  
  388.     return result;
  389. }
  390.  
  391. /************************************************************************************ 
  392.  *    This gets called when the component instance is opened. We allocate our shared storage at this
  393.  *    point. 
  394.  
  395.  *    If we have shared globals, we check if they exist, otherwise we allocate
  396.  *  them and set the ComponentRefCon so that other instances can use them.
  397.  *
  398.  *    The shared globals hold our CodecInfo struct, which we read from our resource file,
  399.  *  and some tables that we use for speed. If we cant get the tables we can work without
  400.  *  them. All items in the shared globals are made purgeable when the last of our 
  401.  *    instances is closed. If our component was loaded in the application heap ( because
  402.  *    there was no room in the system heap) then we keep our shared storage in the app heap.
  403.  *
  404.  *  We keep a pointer to the shared globals in our instance globals so that other calls can get to them.
  405.  */
  406.  
  407.  
  408. ComponentResult
  409. InitSharedTables(Globals *glob,ComponentInstance self)
  410. {
  411.     SharedGlobals    *sGlob;
  412.     long            i,j,*lp;
  413.     char            *cp;
  414.     short            resFile = kResFileNotOpened;
  415.     short            oldResFile;
  416.     THz                saveZone;
  417.     Boolean            inAppHeap;
  418.     OSErr            result = noErr;
  419.         
  420.      
  421.     oldResFile = CurResFile();
  422.     saveZone = GetZone();
  423.     inAppHeap = ( GetComponentInstanceA5(self) != 0 );
  424.     if ( !inAppHeap )
  425.         SetZone(SystemZone());
  426.     if ( (sGlob=(SharedGlobals*)GetComponentRefcon((Component)self)) == nil  ) {
  427.         if ( (sGlob = (SharedGlobals*)NewPtrClear(sizeof(SharedGlobals))) == nil ) { 
  428.             result = MemError();
  429.             goto obail;
  430.         } 
  431.         SetComponentRefcon((Component)self,(long)sGlob);
  432.     }
  433.  
  434.     glob->sharedGlob = sGlob;    // keep this around where it's easy to get at
  435.     
  436.  
  437.     if ( sGlob->info == nil || *(Handle)sGlob->info == nil  )  {
  438.  
  439.         if ( sGlob->info ) 
  440.             DisposeHandle((Handle)sGlob->info);
  441.  
  442.         /* Get the CodecInfo struct which we keep in our resource fork */
  443.         
  444. #ifndef LINK_EXAMPLE_CODEC
  445.         resFile = OpenComponentResFile((Component)self);
  446.         if (resFile == kResFileNotOpened) {
  447.             result = memFullErr;
  448.             goto obail;
  449.         }
  450. #endif
  451. #ifndef POWERPC_NATIVE
  452.         sGlob->info = (CodecInfo **) Get1Resource(codecInfoResourceType,128);
  453. #else
  454.         sGlob->info = (CodecInfo **) Get1Resource(codecInfoResourceType,129);
  455. #endif
  456.         if ( sGlob->info == nil ) {
  457.             result = ResError();
  458.             goto obail;
  459.         }
  460.         LoadResource((Handle)sGlob->info);
  461.         if ( result = ResError() ) {
  462.             goto obail;
  463.         }
  464.         DetachResource((Handle)sGlob->info);
  465.     }
  466.     HNoPurge((Handle)sGlob->info);
  467.     
  468.     if ( sGlob->rgbwTable == nil || *sGlob->rgbwTable == nil )  {
  469.         if ( sGlob->rgbwTable )
  470.             ReallocateHandle(sGlob->rgbwTable,3*256*sizeof(long));
  471.         else 
  472.             sGlob->rgbwTable = NewHandleSys(3*256*sizeof(long));
  473.             
  474.         /* we can actual still work without these tables, so we dont bail
  475.            if we cant get the memory for them */
  476.            
  477.         if ( sGlob->rgbwTable  && *sGlob->rgbwTable ) {
  478.             lp = (long *)*sGlob->rgbwTable;
  479.             for ( i=0, j = 0; i < 256; i++, j += R_W )
  480.                 *lp++ = j;
  481.             for ( i=0, j = 0; i < 256; i++, j += G_W )
  482.                 *lp++ = j;
  483.             for ( i=0, j = 0; i < 256; i++, j += B_W )
  484.                 *lp++ = j;
  485.         }
  486.     }
  487.     if ( sGlob->rgbwTable ) 
  488.         HNoPurge(sGlob->rgbwTable);            /* make sure it wont get purged while we are open */
  489.     
  490.     /* green inverse table */
  491.  
  492.     if ( sGlob->giwTable == nil || *sGlob->giwTable == nil  )  {
  493.         if ( sGlob->giwTable ) 
  494.             ReallocateHandle(sGlob->giwTable,256);
  495.         else 
  496.             sGlob->giwTable = NewHandleSys(256);
  497.  
  498.         /* we can actual still work without these tables, so we dont bail
  499.            if we cant get the memory for them */
  500.  
  501.         if ( sGlob->giwTable && *sGlob->giwTable  ) {
  502.             for ( i=0, cp = *sGlob->giwTable ; i < 256; i++ )
  503.                 *cp++ = PIN( (i<<16) / G_W);
  504.         }
  505.     }
  506.     if ( sGlob->giwTable ) 
  507.         HNoPurge(sGlob->giwTable);            /* make sure it wont get purged while we are open */
  508.  
  509.  
  510. obail:
  511. #ifndef LINK_EXAMPLE_CODEC
  512.     if (resFile != kResFileNotOpened)
  513.         CloseComponentResFile(resFile);
  514. #endif
  515.     UseResFile(oldResFile);
  516.     SetZone(saveZone);
  517.     if ( result != noErr && sGlob != nil ) {
  518.         if ( sGlob->rgbwTable )
  519.             DisposeHandle(sGlob->rgbwTable);
  520.         if ( sGlob->info )
  521.             DisposeHandle((Handle)sGlob->info);
  522.         DisposePtr((Ptr)sGlob);
  523.         SetComponentRefcon((Component)self,(long)nil);
  524.     }
  525.     return(result);
  526. }
  527.  
  528. /************************************************************************************ 
  529.  *    This gets called when the component instance is closed. We need to get rid of any 
  530.  *    instance storage here. 
  531.  */
  532.  
  533. pascal ComponentResult CDClose(Globals *storage,ComponentInstance self)
  534. {
  535.     SharedGlobals    *sGlob;
  536.     Globals            *glob = (Globals *)storage;
  537.         
  538.     /*    If we are closing our last instance 
  539.         then we make the shared global items purgeable to
  540.         speed things up next instance.
  541.      */
  542.     
  543.     if (glob) {
  544.         if ( CountComponentInstances((Component)self) == 1) {
  545.             if ( (sGlob=(SharedGlobals*)glob->sharedGlob) != nil  ) {
  546.                 if ( sGlob->rgbwTable )
  547.                     HPurge(sGlob->rgbwTable);
  548.                 if ( sGlob->giwTable )
  549.                     HPurge(sGlob->giwTable);
  550.                 if ( sGlob->info )
  551.                     HPurge((Handle)sGlob->info);
  552.             }
  553.         }
  554.  
  555. #if ASYNC_DECODE
  556.         if (glob->callBack) {
  557.             CDFlush((void *)glob);
  558.             DisposeCallBack(glob->callBack);
  559.         }
  560. #endif
  561.  
  562.         DisposePtr((Ptr)glob);
  563.     }
  564.     return(noErr);
  565. }
  566.  
  567.  
  568.  
  569. /************************************************************************************ 
  570.  *     Set the target for this component.
  571.  */
  572.  
  573. #if ASYNC_DECODE
  574. pascal ComponentResult CDTarget(Globals *storage,ComponentInstance target)
  575. {
  576.     Globals    *glob = (Globals *)storage;
  577.  
  578.     glob->target = target;
  579.  
  580.     return noErr;
  581. }
  582. #endif
  583.  
  584. /************************************************************************************ 
  585.  *    Return the version of this component ( defines interface ) and revision level
  586.  *    of the code.
  587.  */
  588.  
  589. pascal ComponentResult CDVersion(Globals *storage)
  590. {
  591.     return ((codecInterfaceVersion<<16) | EXAMPLE_CODEC_REV);        /* interface version in hi word, code rev in lo word  */
  592. }
  593.  
  594.  
  595. #if COMP_BUILD
  596.  
  597. /************************************************************************************ 
  598.  *    CDPreCompress gets called before an image is compressed, or whenever the source pixmap
  599.  *    pixel size changes when compressing a sequence. We return information about
  600.  *    how we can compress the image to the codec manager, so that it can fit the source data
  601.  *    to our requirements. The ImageDescriptor is already filled in, so we can use it for 
  602.  *    reference (or even add to it ). PixelSize is the pixel depth of the source pixmap, we
  603.  *    use this as a reference for deciding what we can do. The other parameters return information
  604.  *    to the CodecManager about what we can do. We can also do setup here if we want to.
  605.  */
  606.  
  607. pascal ComponentResult CDPreCompress(Globals *storage,register CodecCompressParams *p)
  608. {
  609.     CodecCapabilities *capabilities = p->capabilities;
  610.  
  611.     /*
  612.      *    First we return which depth input pixels we can deal with - based on what the
  613.      *    app has available - we can only work with 32 bit input pixels.
  614.      */
  615.        
  616.     switch ( (*p->imageDescription)->depth )  {
  617.         case 16:
  618.             capabilities->wantedPixelSize = 32;
  619.             break;
  620.         default:
  621.             return(codecConditionErr);
  622.             break;
  623.     }
  624.  
  625.     /* if the buffer gets banded,  return the smallest one we can deal with */
  626.     
  627.     capabilities->bandMin = 2;
  628.  
  629.     /* if the buffer gets banded, return the increment it be should grown */
  630.  
  631.     capabilities->bandInc = 2;
  632.     
  633.     
  634.     /*
  635.      *    If a codec needs the dimensions of the source pixmap to be of certain multiples
  636.      *    it can ask for the image to be extended out (via pixel replication) vertically
  637.      *    and/or horizontally.
  638.      *
  639.      *    In our case, we're dealing with 2 by 2 blocks and therefore we want the image
  640.      *    height and width to both be multiples of 2.  If either dimension is odd, we
  641.      *    ask it have it extended by one pixel.
  642.      */
  643.  
  644.     capabilities->extendWidth = (*p->imageDescription)->width & 1;
  645.     capabilities->extendHeight = (*p->imageDescription)->height & 1;
  646.     
  647.     return(noErr);
  648. }
  649.  
  650.  
  651. /************************************************************************************ 
  652.  *    CDBandCompress gets called when the codec manager wants us to compress an image, or a horizontal 
  653.  *    band of an image. The pixel data at sBaseAddr is guaranteed to conform to the criteria we 
  654.  *    demanded in BeginCompress.
  655.  */
  656.  
  657. pascal ComponentResult CDBandCompress(Globals *storage,register CodecCompressParams *p)
  658. {
  659.     short                width,height;
  660.     Ptr                    cDataPtr,dataStart;
  661.     short                depth;
  662.     Rect                sRect;
  663.     long                offsetH,offsetV;
  664.     Globals                *glob  = (Globals *)storage;
  665.     char                 *baseAddr;
  666.     long                numLines,numStrips;
  667.     short                rowBytes;
  668.     long                stripBytes;
  669.     char                mmuMode = 1;
  670.     short                y;
  671.     ImageDescription    **desc = p->imageDescription;
  672.     OSErr                result = noErr;
  673.     
  674.     /*    If there is a progress proc, give it an open call at the start of this band. */
  675.  
  676.     if (p->progressProcRecord.progressProc)
  677.         CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressOpen,0,
  678.             p->progressProcRecord.progressRefCon);
  679.  
  680.     width = (*desc)->width;
  681.     height = (*desc)->height;
  682.     depth = (*desc)->depth;
  683.     dataStart = cDataPtr = p->data;
  684.  
  685.     /* figure out offset to first pixel in baseAddr from the pixelsize and bounds */
  686.  
  687.     rowBytes = p->srcPixMap.rowBytes & 0x3fff;
  688.     sRect =  p->srcPixMap.bounds;
  689.     numLines = p->stopLine - p->startLine;        /* number of scanlines in this band */
  690.     numStrips = (numLines+1)>>1;                /* number of strips in this band */
  691.     stripBytes = ((width+1)>>1) * 5;
  692.     
  693.     /* adjust the source baseAddress to be at the beginning of the desired rect */
  694.  
  695.     switch ( p->srcPixMap.pixelSize ) {
  696.     case 32:
  697.         offsetH = sRect.left<<2;
  698.         break;
  699.     case 16:
  700.         offsetH = sRect.left<<1;
  701.         break;
  702.     case 8:
  703.         offsetH = sRect.left;
  704.         break;
  705.     default:
  706.         result = codecErr;
  707.         goto bail;
  708.     }
  709.     offsetV = sRect.top * rowBytes;
  710.     baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV;
  711.  
  712.  
  713.     /* if there is not a flush proc, adjust the pointer to the next band */
  714.     
  715.     if (  p->flushProcRecord.flushProc == nil )
  716.         cDataPtr += (p->startLine>>1) * stripBytes;
  717.     else {
  718.         if ( p->bufferSize < stripBytes ) {
  719.             result = codecSpoolErr;
  720.             goto bail;
  721.         }
  722.     }
  723.  
  724.  
  725.     /* do the slower flush/progress case if we have too */
  726.     
  727.     if (  p->flushProcRecord.flushProc  || p->progressProcRecord.progressProc ) {
  728.         SharedGlobals *sg = glob->sharedGlob;
  729.  
  730.  
  731.         for ( y=0; y < numStrips; y++) {
  732.             SwapMMUMode(&mmuMode);
  733.             CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
  734.             SwapMMUMode(&mmuMode);
  735.             baseAddr += rowBytes<<1;
  736.             if ( p->flushProcRecord.flushProc ) { 
  737.                 if ( (result=CallICMFlushProc(p->flushProcRecord.flushProc,cDataPtr,stripBytes,
  738.                         p->flushProcRecord.flushRefCon)) != noErr) {
  739.                     result = codecSpoolErr;
  740.                     goto bail;
  741.                 }
  742.             } else {
  743.                 cDataPtr += stripBytes;
  744.             }
  745.             if (p->progressProcRecord.progressProc) {
  746.                 if ( (result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent,
  747.                     FixDiv(y,numStrips),p->progressProcRecord.progressRefCon)) != noErr ) {
  748.                         result = codecAbortErr;
  749.                         goto bail;
  750.                     }
  751.             }
  752.         }
  753.     } else {
  754.         SharedGlobals *sg = glob->sharedGlob;
  755.         short    tRowBytes = rowBytes<<1;
  756.  
  757.         SwapMMUMode(&mmuMode);
  758.         for ( y=numStrips; y--; ) {
  759.             CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
  760.             cDataPtr += stripBytes;
  761.             baseAddr += tRowBytes;
  762.         }
  763.         SwapMMUMode(&mmuMode);
  764.     }
  765.  
  766.     /*
  767.     
  768.         return size and similarity on the last band 
  769.         
  770.     */
  771.     
  772.     if ( p->conditionFlags & codecConditionLastBand ) {
  773.         (*p->imageDescription)->dataSize = stripBytes * ((height+1)>>1);    /* return the actual size of the compressed data */
  774.         p->similarity = 0;                            /* we don't do frame differencing */
  775.     }
  776.     
  777. bail:
  778.     /*    If there is a progress proc, give it a close call at the end of this band. */
  779.  
  780.     if (p->progressProcRecord.progressProc)
  781.         CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0,
  782.             p->progressProcRecord.progressRefCon);
  783.         
  784.     return(result);
  785. }
  786.  
  787. #endif
  788.  
  789. pascal ComponentResult CDBusy(Globals *storage, ImageSequence seq)
  790. {
  791. #pragma    unused(seq)
  792. #if ASYNC_DECODE && QT_MP
  793.     // and return true if there is still something goin' on
  794.     return TestBusy( (Globals *)storage );
  795. #else
  796.     return 0;
  797. #endif
  798. }
  799.  
  800. #if ASYNC_DECODE
  801.  
  802. /*
  803.     CDFlush is called when the image compression manager wants the 
  804.     codec to empty its schedule queue. An example would be when a movie is
  805.     playing and the user moves the thumb. The sudden jump in time renders
  806.     any previously scheduled frames useless. So we need to flush the queue so
  807.     we can start over.
  808. */
  809. pascal ComponentResult CDFlush(Globals *storage)
  810. {
  811.     Globals *glob = (Globals *)storage;
  812.  
  813.     /*
  814.         if there's not a callback proc allocated, we sure don't have any frames
  815.         queued up.
  816.     */
  817.     if (glob->callBack) {
  818.         DecompressRecord *drp;
  819.         long saveA5 = SetA5(glob->a5World);
  820.  
  821.         glob->queueIsRunning = false;    // flag queue as not running. 
  822.                                         // otherwise, we'll never start it up again
  823.  
  824.         // kill the callback
  825.         CancelCallBack(glob->callBack);
  826.  
  827. #if QT_MP
  828.         // wait for things to stop
  829.         while( TestBusy( glob ) )
  830.             ;
  831. #endif
  832.  
  833.         // tear down the queue
  834.         while (drp = (DecompressRecord *)glob->busyQueueHead.qHead) {
  835.             drp = (DecompressRecord *)(((long)drp) - fieldOffset(DecompressRecord, nextBusy));
  836.             Dequeue((QElem *)&drp->nextBusy, &glob->busyQueueHead);
  837.  
  838.             if (drp->srcData) {
  839.                 // call back to say we're done
  840.                 ICMDecompressComplete(glob->sequenceID, -1, codecCompletionSource | codecCompletionDest, &drp->completionProc);
  841.                 Enqueue((QElem *)&drp->nextFree, &glob->freeQueueHead);
  842.                 drp->srcData = nil;
  843.             }
  844.         }
  845.  
  846.         SetA5(saveA5);
  847.     }
  848.  
  849.     return (noErr);
  850. }
  851.  
  852. #if QT_MP
  853. pascal ComponentResult CDGetMPWorkFunction(Globals *storage, ComponentMPWorkFunctionUPP *workFunction, void **refCon)
  854. {
  855.     Globals *glob = (Globals *)storage;
  856.  
  857.     *workFunction = (ComponentMPWorkFunctionUPP)GetRoutineAddress(DecompressWorkFunction);
  858.     *refCon = storage;
  859.  
  860.     return noErr;
  861. }
  862. #endif // QT_MP
  863.  
  864. #endif // ASYNC_DECODE
  865.  
  866.  
  867. #if DECO_BUILD
  868.  
  869.  
  870. /************************************************************************************ 
  871.  *    CDPreDecompress gets called before an image is decompressed. We return information about
  872.  *    how we can decompress the image to the codec manager, so that it can fit the destination data
  873.  *    to our requirements. 
  874.  */
  875.  
  876. pascal ComponentResult CDPreDecompress(Globals *storage,register CodecDecompressParams *p)
  877. {
  878.     Globals    *glob  = (Globals *)storage;
  879.     register CodecCapabilities    *capabilities = p->capabilities;
  880.  
  881.     /*    Decide which depth compressed data we can deal with. */
  882.     
  883.     switch ( (*p->imageDescription)->depth )  {
  884.         case 16:
  885.             break;
  886.         default:
  887.             return(codecConditionErr);
  888.             break;
  889.     }
  890.     
  891.     /*    We can deal only 32 bit pixels. */
  892.  
  893.     capabilities->wantedPixelSize = 32;    
  894.     
  895.     /*    The smallest possible band we can do is 2 scan lines. */
  896.     
  897.     capabilities->bandMin = 2;
  898.  
  899.     /*    We can deal with 2 scan line high bands. */
  900.  
  901.     capabilities->bandInc = 2;
  902.     
  903.     /*    If we needed our pixels to be aligned on some integer multiple we would set these to
  904.      *    the number of pixels we need the dest extended by. If we dont care, or we take care of
  905.      *  it ourselves we set them to zero.
  906.      */
  907.  
  908.     capabilities->extendWidth = p->srcRect.right & 1;
  909.     capabilities->extendHeight = p->srcRect.bottom & 1;
  910. #if ASYNC_DECODE
  911.     if (glob->decompressFunction == nil) {
  912. #if QT_MP
  913.         CallComponentGetMPWorkFunction(glob->target, &glob->decompressFunction, &glob->decompressFunctionRefCon);
  914. #else
  915.         glob->decompressFunction = (ComponentMPWorkFunctionUPP)GetRoutineAddress(DecompressWorkFunction);
  916.         glob->decompressFunctionRefCon = glob;
  917. #endif
  918.     }
  919.  
  920.     capabilities->flags = codecCanAsyncWhen | codecCanAsync | codecCanShieldCursor;
  921.     glob->sequenceID = p->sequenceID;
  922. #else
  923.     capabilities->flags = 0;
  924. #endif
  925.     return(noErr);
  926. }
  927.  
  928. #if ASYNC_DECODE
  929.  
  930. pascal ComponentResult DecompressWorkFunction(Globals *glob, DecompressRecord *drp)
  931. {
  932.     if (drp->header.workFlags & mpWorkFlagGetProcessorCount)
  933.         drp->header.processorCount = 1;
  934.  
  935.     if (drp->header.workFlags & mpWorkFlagDoWork) {
  936.         short y;
  937.         char mmuMode = true32b;            // we want to be in 32-bit mode
  938.         Ptr cDataPtr = drp->srcData;        // compressed data pointer;
  939.         Ptr baseAddr = drp->baseAddr;        // base address of dest PixMap;
  940.  
  941.         SwapMMUMode(&mmuMode);            // put us in 32-bit mode
  942.         for ( y=drp->numStrips; y--; ) {
  943.             DecompressStrip(cDataPtr,baseAddr,drp->rowBytes,drp->width,glob->sharedGlob);
  944.             baseAddr += drp->baseAddrIncrement;
  945.             cDataPtr += drp->srcDataIncrement;
  946.         }
  947.         SwapMMUMode(&mmuMode);            // back to our old memory mode
  948.     }
  949.  
  950.     if (drp->header.workFlags & mpWorkFlagDoCompletion) {
  951.         glob->decompressCount--;
  952.  
  953.         ICMDecompressComplete(glob->sequenceID, noErr, codecCompletionSource | codecCompletionDest, &drp->completionProc);
  954.  
  955.         drp->srcData = nil;
  956.  
  957.         if (drp->inQueue) {
  958.             Dequeue((void *)&drp->nextBusy, &glob->busyQueueHead);
  959.             Enqueue((void *)&drp->nextFree, &glob->freeQueueHead);
  960.  
  961.             // queue up the next one
  962.             if (glob->queueIsRunning && (drp = (void *)glob->busyQueueHead.qHead)) {
  963.                 drp = (void *)((Ptr)drp - (Ptr)fieldOffset(DecompressRecord, nextBusy));
  964.                 CallMeWhen(
  965.                         glob->callBack,
  966.                         glob->decompressCallBackUPP,
  967.                         (long)drp,
  968.                         (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
  969.                         (long)drp->frameTime,
  970.                         (long)drp->scale);
  971.             }
  972.             else {
  973.                 glob->queueIsRunning = false;
  974.             }
  975.         }
  976.     }
  977.  
  978.     return noErr;
  979. }
  980.  
  981. /*
  982.     This is the call back proc that is used for scheduled asynchronously displayed
  983.     frames. It is called at the scheduled frame time, and should display that
  984.     frame. If there is a frame queued up after this one, it should be scheduled.
  985. */
  986. pascal void DecompressCallBack(QTCallBack cb,long refcon)
  987. {
  988.     DecompressRecord *drp = (DecompressRecord *)refcon;
  989.     Globals *glob = (Globals *)drp->glob;
  990.  
  991.     if (drp->srcData) {
  992.         if (drp->shieldCursor) {
  993.             ICMShieldSequenceCursor(glob->sequenceID);
  994.             drp->shieldCursor = false;
  995.         }
  996.  
  997.         drp->header.headerSize = sizeof(ComponentMPWorkFunctionHeaderRecord);
  998.         drp->header.recordSize = sizeof(DecompressRecord);
  999.         drp->header.workFlags = mpWorkFlagDoWork | mpWorkFlagDoCompletion;
  1000.         if (!drp->inQueue)
  1001.             drp->header.workFlags |= mpWorkFlagCopyWorkBlock;
  1002.  
  1003.         glob->decompressCount++;
  1004.         CallComponentMPWorkFunctionProc(glob->decompressFunction, glob->decompressFunctionRefCon, (void *)drp);
  1005.     }
  1006. }
  1007.  
  1008. #endif
  1009.  
  1010.  
  1011. /************************************************************************************ 
  1012.  *    CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal 
  1013.  *    band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we 
  1014.  *    demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and
  1015.  *    we need to clear bits in it that correspond to any pixels in the destination we do not want to 
  1016.  *    change. ( We always write all pixels, so we dont care. This mode is important only for those
  1017.  *    codecs that have frame differencing and don't always write all the pixels. )
  1018.  */
  1019.  
  1020. pascal ComponentResult CDBandDecompress(Globals *storage,register CodecDecompressParams *p)
  1021. {
  1022.     long                offsetH,offsetV;
  1023.     Globals                *glob  = (Globals *)storage;
  1024.     long                numLines,numStrips;
  1025.     short                rowBytes;
  1026.     long                stripBytes;
  1027.     short                width;
  1028.     short                y;
  1029.     char                *baseAddr;
  1030.     char                *cDataPtr;
  1031.     char                mmuMode = 1;
  1032.     OSErr                result = noErr;
  1033.     
  1034.     /*    If there is a progress proc, give it an open call at the start of this band. */
  1035.  
  1036.     if (p->progressProcRecord.progressProc)
  1037.         CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressOpen,0,
  1038.             p->progressProcRecord.progressRefCon);
  1039.  
  1040.  
  1041.  
  1042.     /* initialize some local variables */
  1043.     
  1044.     width = (*p->imageDescription)->width;
  1045.     rowBytes = p->dstPixMap.rowBytes;                    
  1046.     numLines = p->stopLine - p->startLine;            /* number of scanlines in this band */
  1047.     numStrips = (numLines+1)>>1;                    /* number of strips in this band */
  1048.     stripBytes = ((width+1)>>1) * 5;                /* number of bytes in one strip of blocks */
  1049.     cDataPtr = p->data;
  1050.     
  1051.     /* adjust the destination baseaddress to be at the beginning of the desired rect */
  1052.     
  1053.     offsetH = (p->dstRect.left - p->dstPixMap.bounds.left);
  1054.     offsetH <<=2;                    /* 1 pixel = 4 bytes */
  1055.     offsetV = (p->dstRect.top - p->dstPixMap.bounds.top) * rowBytes;
  1056.     baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
  1057.  
  1058.  
  1059.     /* 
  1060.      *    If we are skipping some data, we just skip it here. We can tell because
  1061.      *    firstBandInFrame says this is the first band for a new frame, and
  1062.      *    if startLine is not zero, then that many lines were clipped out.
  1063.      */
  1064.  
  1065.     if ( (p->conditionFlags & codecConditionFirstBand) && p->startLine != 0 ) {
  1066.         if ( p->dataProcRecord.dataProc ) {
  1067.             for ( y=0; y  < p->startLine>>1; y++ )  {
  1068.                 if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes,
  1069.                         p->dataProcRecord.dataRefCon)) != noErr ) {
  1070.                     result = codecSpoolErr;
  1071.                     goto bail;
  1072.                 }
  1073.                 cDataPtr += stripBytes;
  1074.             }
  1075.         } else
  1076.             cDataPtr += (p->startLine>>1) * stripBytes;
  1077.     }
  1078.     
  1079.     /*
  1080.      *    If theres a dataproc spooling the data to us, then we have to do the data
  1081.      *    in whatever size chunks they want to give us, or if there is a progressProc
  1082.      *  make sure to call it as we go along.
  1083.      */
  1084.     
  1085.     if ( p->dataProcRecord.dataProc || p->progressProcRecord.progressProc ) {
  1086.         SharedGlobals *sg = glob->sharedGlob;
  1087.     
  1088. #if ASYNC_DECODE
  1089.         if (p->conditionFlags & codecConditionDoCursor)
  1090.             ICMShieldSequenceCursor(p->sequenceID);
  1091. #endif
  1092.  
  1093.         for (y=0; y < numStrips; y++) {
  1094.             if (p->dataProcRecord.dataProc) {
  1095.                 if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes,
  1096.                         p->dataProcRecord.dataRefCon)) != noErr ) {
  1097.                     result = codecSpoolErr;
  1098.                     goto bail;
  1099.                 }
  1100.             }
  1101.             SwapMMUMode(&mmuMode);
  1102.             DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
  1103.             SwapMMUMode(&mmuMode);
  1104.             baseAddr += rowBytes<<1;
  1105.             cDataPtr += stripBytes;
  1106.             if (p->progressProcRecord.progressProc) {
  1107.                 if ( (result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent,
  1108.                     FixDiv(y, numStrips),p->progressProcRecord.progressRefCon)) != noErr ) {
  1109.                     result = codecAbortErr;
  1110.                      goto bail;
  1111.                 }
  1112.             }
  1113.         }
  1114.         ICMDecompressComplete(p->sequenceID, noErr, codecCompletionSource | codecCompletionDest, &p->completionProcRecord);
  1115.  
  1116.     /* 
  1117.      *
  1118.      * otherwise - do the fast case. 
  1119.      *
  1120.      */
  1121.          
  1122.     } else {
  1123.         DecompressRecord stackDecompressRecord;
  1124.         DecompressRecord *drp = &stackDecompressRecord;
  1125.  
  1126. #if ASYNC_DECODE
  1127.         if (p->frameTime) {
  1128.             if (!glob->freeQueueHead.qHead) {
  1129.                 result = codecCantQueueErr;
  1130. errorReturn:
  1131.                 ICMDecompressComplete(p->sequenceID, result, codecCompletionSource | codecCompletionDest, &p->completionProcRecord);
  1132.                 goto bail;
  1133.             }
  1134.  
  1135.             if (!glob->callBack) {
  1136.                 // there is no current callback. allocate one.
  1137.                 glob->callBack = NewCallBack(p->frameTime->base,callBackAtTime + callBackAtInterrupt + callBackAtDeferredTask);
  1138.                 if (!glob->callBack) {
  1139.                     result = codecCantQueueErr;
  1140.                     goto errorReturn;
  1141.                 }
  1142.                 glob->a5World = SetA5(0);
  1143.                 SetA5(glob->a5World);            // is there a way to get a5 without changing it?
  1144.             }
  1145.             drp = (void *)((Ptr)glob->freeQueueHead.qHead - (Ptr)fieldOffset(DecompressRecord, nextFree));
  1146.             Dequeue((void *)&drp->nextFree, &glob->freeQueueHead);
  1147.         }
  1148. #endif // ASYNC_DECODE
  1149.  
  1150.         drp->srcData = cDataPtr;
  1151.         drp->baseAddr = baseAddr;
  1152.         drp->rowBytes = rowBytes;
  1153.         drp->width = width;
  1154.         drp->numStrips = numStrips;
  1155.         drp->srcDataIncrement = stripBytes;
  1156.         drp->baseAddrIncrement = rowBytes<<1;
  1157.         drp->completionProc = p->completionProcRecord;
  1158.         drp->frameTime = p->frameTime->value.lo;
  1159.         drp->scale = p->frameTime->scale;
  1160.         drp->rate = p->frameTime->rate;
  1161.         drp->shieldCursor = (p->conditionFlags & codecConditionDoCursor) != 0;
  1162.         drp->glob = (void *)glob;
  1163.  
  1164.  
  1165. #if ASYNC_DECODE
  1166.         glob->sequenceID = p->sequenceID;
  1167.  
  1168.         if (p->frameTime) {
  1169.             drp->inQueue = true;
  1170.             Enqueue((void *)(&drp->nextBusy), &glob->busyQueueHead);    // put the frame in the queue
  1171.             if (!glob->queueIsRunning) {
  1172.                 // the queue isn't running... start it up
  1173.                 glob->queueIsRunning = true;    // since CallMeWhen could be considered "CallMeRightNow"
  1174.                 if (result = CallMeWhen(
  1175.                         glob->callBack,
  1176.                         glob->decompressCallBackUPP,
  1177.                         (long)drp,
  1178.                         (drp->rate >= 0) ? triggerTimeFwd : triggerTimeBwd,
  1179.                         (long)drp->frameTime,
  1180.                         (long)drp->scale)) {
  1181.                     // there was an error
  1182.                     glob->queueIsRunning = false;    // if error, queue isn't running
  1183.                     drp->srcData = nil;
  1184.                     Dequeue((void *)&drp->nextBusy, &glob->busyQueueHead);
  1185.                     Enqueue((void *)&drp->nextFree, &glob->freeQueueHead);
  1186.                     goto errorReturn;
  1187.                 }
  1188.             }
  1189.         } 
  1190.         else 
  1191. #endif // ASYNC_DECODE
  1192.         {
  1193.             drp->inQueue = false;
  1194.  
  1195.             DecompressCallBack(nil, (long)drp);
  1196.             cDataPtr += *(long *)p->data & 0x00FFFFFF;
  1197.         }
  1198.     }
  1199.     
  1200.     /* 
  1201.      *
  1202.      *  IMPORTANT: update pointer to data in params, so when we get the  next
  1203.      *  band we'll be at the right place in our data.
  1204.      *  
  1205.      */
  1206.     
  1207.     p->data = cDataPtr;
  1208.     
  1209.     if ( p->conditionFlags & codecConditionLastBand ) {
  1210.         /* Tie up any loose ends on the last band of the frame, if we had any */
  1211.     }
  1212.  
  1213. bail:
  1214.     /*    If there is a progress proc, give it a close call at the end of this band. */
  1215.  
  1216.     if (p->progressProcRecord.progressProc)
  1217.         CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0,
  1218.             p->progressProcRecord.progressRefCon);
  1219.  
  1220.     return result;
  1221. }
  1222.  
  1223. #endif // DECO_BUILD
  1224.  
  1225. /************************************************************************************ 
  1226.  *    CDGetCodecInfo allows us to return information about ourselves to the codec manager.
  1227.  *    
  1228.  *    There will be a tool for determining appropriate values for the accuracy, speed
  1229.  *    and level information. For now we estimate with scientific wild guessing.
  1230.  *
  1231.  *  The info is stored as a resource in the same file with our component.
  1232.  */
  1233.  
  1234. pascal ComponentResult CDGetCodecInfo(Globals *storage,CodecInfo *info)
  1235. {
  1236.     Globals *glob = (Globals *)storage;
  1237.  
  1238.     if ( info == nil ) 
  1239.         return(paramErr);
  1240.     BlockMoveData((Ptr)*(glob->sharedGlob)->info,(Ptr)info,sizeof(CodecInfo));
  1241.     return(noErr);
  1242. }
  1243.  
  1244.  
  1245. #if DECO_BUILD
  1246. /************************************************************************************ 
  1247.  *    When CDGetCompressedImageSize is called, we return the size in bytes of the given compressed
  1248.  *    data ( for one image frame).
  1249.  */
  1250.  
  1251. pascal ComponentResult CDGetCompressedImageSize(Globals *storage,ImageDescriptionHandle desc,Ptr data,
  1252.     long dataSize, ICMDataProcRecordPtr dataProc,long *size)
  1253. {
  1254. #pragma    unused(storage,data,dataSize,dataProc)
  1255.  
  1256.     short    width =(*desc)->width;
  1257.     short    height = (*desc)->height;
  1258.     
  1259.     if ( size == nil )
  1260.         return(paramErr);
  1261.         
  1262.     /*
  1263.      *    Our data has a size which is deterministic based on the image size. If it were not we
  1264.      *    could encode the size in the compressed data, or figure it out by walking the
  1265.      *    compressed data.
  1266.      */
  1267.      
  1268.     *size = ((width+1)/2) * 5 * ((height+1)/2);
  1269.     return(noErr);
  1270. }
  1271. #endif // DECO_BUILD
  1272.  
  1273. #if COMP_BUILD
  1274. /************************************************************************************ 
  1275.  *    When CDGetMaxCompressionSize is called, we return the maximum size the compressed data for
  1276.  *    the given image would be in bytes.
  1277.  */
  1278.  
  1279. pascal ComponentResult CDGetMaxCompressionSize(Globals *storage,PixMapHandle src,
  1280.     const Rect *srcRect,short depth, CodecQ quality,long *size)
  1281. {
  1282. #pragma    unused(storage,src,depth,quality)
  1283.     
  1284.     short width = srcRect->right - srcRect->left;
  1285.     short height = srcRect->bottom - srcRect->top;
  1286.  
  1287.     /*    we always end up with a fixed size. If we did not, we would return the worst case size */
  1288.     
  1289.     *size = ((width+1)/2) * 5 * ((height+1)/2);    
  1290.  
  1291.     return(noErr);
  1292. }
  1293.  
  1294.  
  1295. /************************************************************************************ 
  1296.  *    When CDGetCompressionTime is called, we return the approximate time for compressing
  1297.  *    the given image would be in milliseconds. We also return the closest actual quality
  1298.  *    we can handle for the requested value.
  1299.  */
  1300.  
  1301. pascal ComponentResult CDGetCompressionTime(Globals *storage,PixMapHandle src,
  1302.         const Rect *srcRect,short depth,CodecQ *spatialQuality,CodecQ *temporalQuality,
  1303.         unsigned long *time)
  1304. {
  1305. #pragma    unused(storage,src,srcRect,depth)
  1306.  
  1307.     if (time)
  1308.         *time = 0;                                    /* we don't know how many msecs */
  1309.  
  1310.     if (spatialQuality)
  1311.         *spatialQuality = codecNormalQuality;        /* we have only one quality level for now */
  1312.     
  1313.     if (temporalQuality)
  1314.         *temporalQuality = 0;                        /* we cannot do temporal compression */
  1315.  
  1316.     return(noErr);
  1317. }
  1318.  
  1319. #endif // COMP_BUILD
  1320.  
  1321. #if DECO_BUILD
  1322.  
  1323. /************************************************************************************ 
  1324.  *    When CDTrimImage is called, we take the given compressed data and return only the portion
  1325.  *    which is represented by the trimRect. We can return a little more if we have too, but we
  1326.  *    need only return enough so that the image in trimRect is properly displayed. We then
  1327.  *    adjust the rectangle to corresond to the same rectangle in the new trimmed data.
  1328.  */
  1329.  
  1330. pascal ComponentResult CDTrimImage(Globals *storage,ImageDescriptionHandle desc,Ptr inData,
  1331.         long inDataSize,ICMDataProcRecordPtr dataProc,Ptr outData,long outDataSize,
  1332.         ICMFlushProcRecordPtr flushProc,Rect *trimRect,ICMProgressProcRecordPtr progressProc)
  1333. {
  1334. #pragma    unused(storage)
  1335.  
  1336.     Rect    rect = *trimRect;
  1337.     char    *dataP,*odP,*startP;
  1338.     short    trimOffTop;
  1339.     short    trimOffBottom;
  1340.     short    trimOffLeft;
  1341.     short    trimOffRight;
  1342.     short    bytesOffLeft;
  1343.     short    newHeight,newWidth;
  1344.     long    size;
  1345.     short    stripBytes;
  1346.     short    newStripBytes;
  1347.     short    i,y;
  1348.     OSErr    result = noErr;
  1349.     
  1350.         
  1351.     if ( dataProc->dataProc == nil )
  1352.         dataProc = nil;
  1353.     if ( flushProc->flushProc == nil )
  1354.         flushProc = nil;
  1355.     if ( progressProc->progressProc == nil )
  1356.         progressProc = nil;
  1357.     if ( progressProc ) 
  1358.         CallICMProgressProc(progressProc->progressProc,codecProgressOpen,0,progressProc->progressRefCon);
  1359.  
  1360.     dataP = inData;
  1361.     newHeight = (*desc)->height;
  1362.     newWidth = (*desc)->width;
  1363.     stripBytes = ((newWidth+1)>>1) * 5;            /* the number of bytes in a strip (2-scanlines/strip) */
  1364.     
  1365.     /* figure out how many 2x2 blocks we want to strip from each side of the image */
  1366.  
  1367.     trimOffTop = rect.top>>1;
  1368.     trimOffBottom  = (newHeight - rect.bottom) >> 1;
  1369.     trimOffLeft = rect.left>>1;
  1370.     trimOffRight  = (newWidth - rect.right) >> 1;
  1371.  
  1372.     /* point to the start of the first strip we are using */
  1373.  
  1374.     startP  = dataP + stripBytes * trimOffTop;
  1375.  
  1376.  
  1377.     /* make the trim values pixel based */
  1378.     
  1379.     trimOffLeft <<= 1;
  1380.     trimOffTop <<= 1;
  1381.     trimOffBottom <<= 1;
  1382.     trimOffRight <<= 1;
  1383.     
  1384.     /* calculate new height and width */
  1385.     
  1386.     newHeight -= trimOffTop + trimOffBottom;
  1387.     newWidth -=  trimOffLeft + trimOffRight;
  1388.     
  1389.     /* calc size in bytes of strips of the new width */
  1390.     
  1391.     newStripBytes = ((newWidth+1)>>1) * 5;        
  1392.  
  1393.     /* figure number of bytes to toss at the beginning of each strip  */
  1394.     
  1395.     bytesOffLeft = (trimOffLeft>>1) * 5;
  1396.  
  1397.     /* figure size of new trimmed image */
  1398.     
  1399.     size = newStripBytes * (newHeight>>1);
  1400.     
  1401.     /* make sure it's gonna fit */
  1402.     
  1403.     if ( size > outDataSize )  {
  1404.         result = codecErr;
  1405.         goto bail;
  1406.     }
  1407.         
  1408.     /* now go through the strips and copy the needed portion of each to the new data */
  1409.  
  1410.     if (  dataProc ) {
  1411.         short rightBytes = stripBytes - newStripBytes - bytesOffLeft;
  1412.         for ( y=0; y < trimOffTop; y++ ) {
  1413.             if ( (result=CallICMDataProc(dataProc->dataProc,&inData,stripBytes,dataProc->dataRefCon)) != noErr )
  1414.                 goto bail;
  1415.             inData += stripBytes;
  1416.             if (progressProc ) {
  1417.                 if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent,
  1418.                     FixDiv(y, (*desc)->height),progressProc->progressRefCon)) != noErr)  {
  1419.                     result = codecAbortErr;
  1420.                     goto bail;
  1421.                 }
  1422.             }
  1423.         }
  1424.         for ( y=0; y < newHeight; y+= 2) {
  1425.             if ( bytesOffLeft ) {
  1426.                 if ( (result=CallICMDataProc(dataProc->dataProc,&inData,bytesOffLeft,dataProc->dataRefCon)) != noErr )
  1427.                     goto bail;
  1428.                 inData += bytesOffLeft;
  1429.             }
  1430.             if ( (result=CallICMDataProc(dataProc->dataProc,&inData,newStripBytes,dataProc->dataRefCon)) != noErr )
  1431.                 goto bail;
  1432.             if (  flushProc ) {
  1433.                 if ( (result=CallICMFlushProc(flushProc->flushProc,inData,newStripBytes,flushProc->flushRefCon)) != noErr ) {
  1434.                     result = codecSpoolErr;
  1435.                     goto bail;
  1436.                 }
  1437.             }
  1438.             else {
  1439.                 BlockMoveData(inData,outData,newStripBytes);
  1440.                 outData += newStripBytes;
  1441.             }
  1442.             inData += newStripBytes;
  1443.             if ( rightBytes ) {
  1444.                 if ( (result=CallICMDataProc(dataProc->dataProc,&inData,rightBytes,dataProc->dataRefCon)) != noErr ) {
  1445.                     result = codecSpoolErr;
  1446.                     goto bail;
  1447.                 }
  1448.                 inData += rightBytes;
  1449.             }
  1450.             if (progressProc) {
  1451.                 if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent,
  1452.                     FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr ) {
  1453.                     result = codecAbortErr;
  1454.                     goto bail;
  1455.                 }
  1456.             }
  1457.         }
  1458.     }
  1459.     else {
  1460.         inData += stripBytes * trimOffTop;
  1461.         for ( y=0; y < newHeight; y += 2, inData += stripBytes) {
  1462.             if (  flushProc ) {
  1463.                 if ( (result=CallICMFlushProc(flushProc->flushProc,inData + bytesOffLeft,newStripBytes,flushProc->flushRefCon)) != noErr ) {
  1464.                     result = codecSpoolErr;
  1465.                     goto bail;
  1466.                 }
  1467.             }
  1468.             else {
  1469.                 BlockMoveData(inData + bytesOffLeft,outData,newStripBytes);
  1470.                 outData += newStripBytes;
  1471.             }
  1472.             if (progressProc ) {
  1473.                 if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent,
  1474.                     FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr ) {
  1475.                     result = codecAbortErr;
  1476.                     goto bail;
  1477.                 }
  1478.             }
  1479.         }
  1480.     }
  1481.  
  1482.     /* adjust the rectangle to reflect our changes */
  1483.     
  1484.     trimRect->top -= trimOffTop;
  1485.     trimRect->bottom -= trimOffTop;
  1486.     trimRect->left -= trimOffLeft;
  1487.     trimRect->right -= trimOffLeft;
  1488.  
  1489.     /* return the new width and height in the image description and the size */
  1490.     
  1491.     (*desc)->height = newHeight;
  1492.     (*desc)->width = newWidth;
  1493.     (*desc)->dataSize = size;
  1494. bail:
  1495.     if ( progressProc ) 
  1496.         CallICMProgressProc(progressProc->progressProc,codecProgressClose,0,progressProc->progressRefCon);
  1497.  
  1498.     return(result);
  1499.  
  1500.  
  1501. }
  1502. #endif // DECO_BUILD
  1503.     
  1504. #if NOASM        /* we could do this part in assembly for speed if we desired */
  1505.  
  1506. #if COMP_BUILD
  1507.  
  1508. #define    READPIXEL(n)                \
  1509.     l = *lp++;                        \
  1510.     r = (l>>16);                    \
  1511.     g = (l>>8);                        \
  1512.     b = l;                            \
  1513.     yt = (R_W*r + G_W*g + B_W*b);    \
  1514.     if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \
  1515.     ys[n] = yt>>16;                    \
  1516.     y += yt;                        \
  1517.     u += r;                            \
  1518.     v += b;                        
  1519.  
  1520. #define    READPIXEL_TAB(n)                \
  1521.     l = *lp++;                        \
  1522.     r = (l>>16);                    \
  1523.     g = (l>>8);                        \
  1524.     b = l;                            \
  1525.     yt = (rwTable[r] + gwTable[g] + bwTable[b]);    \
  1526.     if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \
  1527.     ys[n] = yt>>16;                    \
  1528.     y += yt;                        \
  1529.     u += r;                            \
  1530.     v += b;                        
  1531.  
  1532.  
  1533. pascal void
  1534. CompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg)
  1535. {
  1536.  
  1537.     register long    l,*lp = (long *)baseAddr;
  1538.     register unsigned char     r,g,b;
  1539.     unsigned char    ys[4];
  1540.     register long    y,yt;
  1541.     short    u,v;
  1542.     short    rowLongs = (rowBytes>>2);
  1543.         
  1544.         
  1545.     
  1546.     
  1547.     len++;
  1548.     len>>=1;
  1549.     
  1550.     if ( sg->rgbwTable && *sg->rgbwTable  ) {
  1551.         long    *rwTable,*gwTable,*bwTable;
  1552.     
  1553.         rwTable = (long *)*sg->rgbwTable;
  1554.         gwTable = rwTable + 256;
  1555.         bwTable = rwTable + 512;
  1556.  
  1557.         while ( len-- > 0) {
  1558.             y = u = v = 0;
  1559.             READPIXEL_TAB(0);
  1560.             READPIXEL_TAB(1);
  1561.             lp += rowLongs-2;
  1562.             READPIXEL_TAB(2);
  1563.             READPIXEL_TAB(3);
  1564.             lp -= rowLongs;
  1565.         
  1566.             y >>= 16;
  1567.             u = (u - y)>>4;
  1568.             v = (v - y)>>4;
  1569.             
  1570.             l =  (long)(0xfc & (ys[0])) << 24;
  1571.             l |= (long)(0xfc & (ys[1])) << 18;
  1572.             l |= (long)(0xfc & (ys[2])) << 12;
  1573.             l |= (long)(0xfc & (ys[3])) <<  6;
  1574.             l |= u & 0xff;
  1575.             *(long *)data = l;
  1576.             data += sizeof(long);
  1577.             *data++ = v;
  1578.         }
  1579.     } else {
  1580.         while ( len-- > 0) {
  1581.             y = u = v = 0;
  1582.             READPIXEL(0);
  1583.             READPIXEL(1);
  1584.             lp += rowLongs-2;
  1585.             READPIXEL(2);
  1586.             READPIXEL(3);
  1587.             lp -= rowLongs;
  1588.         
  1589.             y >>= 16;
  1590.             u = (u - y)>>4;
  1591.             v = (v - y)>>4;
  1592.             
  1593.             l =  (long)(0xfc & (ys[0])) << 24;
  1594.             l |= (long)(0xfc & (ys[1])) << 18;
  1595.             l |= (long)(0xfc & (ys[2])) << 12;
  1596.             l |= (long)(0xfc & (ys[3])) <<  6;
  1597.             l |= u & 0xff;
  1598.             *(long *)data = l;
  1599.             data += sizeof(long);
  1600.             *data++ = v;
  1601.         }
  1602.     }
  1603. }
  1604.  
  1605. #endif // COMP_BUILD
  1606.  
  1607. #if DECO_BUILD
  1608. #define    WRITEPIXEL                \
  1609.     r = PIN(u+y);                \
  1610.     b = PIN(v+y);                \
  1611.     y <<= 16;                    \
  1612.     y -= r * R_W;                \
  1613.     y -= b * B_W;                \
  1614.     g = PIN(y / G_W);            \
  1615.     *lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b;    
  1616.  
  1617. #define    WRITEPIXEL_TAB            \
  1618.     r = PIN(u+y);                \
  1619.     b = PIN(v+y);                \
  1620.     y <<= 16;                    \
  1621.     y -= rwTable[r];        \
  1622.     y -= bwTable[b];        \
  1623.     g = giwTable[PIN(y>>16)];    \
  1624.     *lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b;    
  1625.     
  1626. pascal void
  1627. DecompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg)
  1628. {
  1629.     register long    y;
  1630.     register unsigned char     r,g,b;
  1631.     register long    l,*lp;
  1632.     long     u,v;
  1633.     unsigned char    ys[4];
  1634.     short    rowLongs = (rowBytes>>2);
  1635.     short    blen = len;
  1636.  
  1637.     lp = (long *)baseAddr;
  1638.     blen++;
  1639.     blen >>= 1;
  1640.     
  1641.     if ( sg->rgbwTable && *sg->rgbwTable &&  sg->giwTable && *sg->giwTable ) {
  1642.         unsigned char    *giwTable;    
  1643.         long    *rwTable,*bwTable;
  1644.  
  1645.         giwTable = (unsigned char *)(*sg->giwTable);
  1646.         rwTable = (long *)*sg->rgbwTable;
  1647.         bwTable = rwTable + 512;
  1648.         while ( blen-- > 0 ) {
  1649.             l = *(long *)data;
  1650.             data += sizeof(long);
  1651.             ys[0] = (0xfc & (l>>24));
  1652.             ys[1] = (0xfc & (l>>18));
  1653.             ys[2] = (0xfc & (l>>12));
  1654.             ys[3] = (0xfc & (l>>6));
  1655.             u = (char)l;
  1656.             v = *data++;
  1657.             u<<=2;
  1658.             v<<=2;
  1659.             y = ys[0];
  1660.             WRITEPIXEL_TAB;
  1661.             y = ys[1];
  1662.             WRITEPIXEL_TAB;
  1663.             lp += rowLongs - 2;
  1664.             y = ys[2];
  1665.             WRITEPIXEL_TAB;
  1666.             y = ys[3];
  1667.             WRITEPIXEL_TAB;
  1668.             lp -= rowLongs;
  1669.         }
  1670.     } else {
  1671.         while ( blen-- > 0 ) {
  1672.             l = *(long *)data;
  1673.             data += sizeof(long);
  1674.             ys[0] = (0xfc & (l>>24));
  1675.             ys[1] = (0xfc & (l>>18));
  1676.             ys[2] = (0xfc & (l>>12));
  1677.             ys[3] = (0xfc & (l>>6));
  1678.             u = (char)l;
  1679.             v = *data++;
  1680.             u<<=2;
  1681.             v<<=2;
  1682.             y = ys[0];
  1683.             WRITEPIXEL;
  1684.             y = ys[1];
  1685.             WRITEPIXEL;
  1686.             lp += rowLongs - 2;
  1687.             y = ys[2];
  1688.             WRITEPIXEL;
  1689.             y = ys[3];
  1690.             WRITEPIXEL;
  1691.             lp -= rowLongs;
  1692.         }
  1693.     }
  1694. }
  1695.  
  1696. #endif // DECO_BUILD
  1697.  
  1698. #endif // NOASM
  1699.  
  1700.  
  1701. #ifdef LINK_EXAMPLE_CODEC
  1702.  
  1703. void InstallExampleCodec(void);
  1704. void InstallExampleCodec(void)
  1705.  
  1706. {
  1707.  
  1708.     ComponentDescription td;
  1709.     Component    c;
  1710.     Handle    cname;
  1711.     Handle    dname;
  1712.  
  1713. #if COMP_BUILD
  1714.     cname = NewHandle(13);
  1715.     td.componentType = 'imco';
  1716.     td.componentSubType = 'exmp';
  1717.     td.componentManufacturer = 'appl';
  1718.     td.componentFlags = codecInfoDoes32;
  1719.     td.componentFlagsMask = 0;
  1720.     
  1721.     BlockMoveData("\pTEST EX COMP",*cname,13);
  1722.     if ((c= RegisterComponent(&td,NewComponentRoutineProc(EXAMPLECODEC), 0,cname,nil, nil)) == 0 ) {
  1723.         Debugger();
  1724.         ExitToShell();
  1725.     }
  1726.     SetDefaultComponent(c,defaultComponentAnyManufacturer+defaultComponentAnyFlags);
  1727. #endif
  1728.  
  1729. #if DECO_BUILD
  1730.     dname = NewHandle(13);
  1731.     td.componentType = 'imdc';
  1732.     td.componentSubType = 'exmp';
  1733.     td.componentManufacturer = 'appl';
  1734.     td.componentFlags = codecInfoDoes32;
  1735.     td.componentFlagsMask = 0;
  1736.  
  1737.     BlockMoveData("\pTEST EX DECO",*dname,13);
  1738.     if ((c= RegisterComponent(&td,NewComponentRoutineProc(EXAMPLECODEC), 0,dname,nil, nil)) == 0 ) {
  1739.         Debugger();
  1740.         ExitToShell();
  1741.     }
  1742.     SetDefaultComponent(c,defaultComponentAnyManufacturer+defaultComponentAnyFlags);
  1743. #endif
  1744. }
  1745. #endif
  1746.  
  1747. #if QT_MP && ASYNC_DECODE
  1748.  
  1749. Boolean TestBusy( Globals *glob )
  1750. {
  1751.     ComponentMPWorkFunctionHeaderRecord work;
  1752.  
  1753.     // give the MP stuff a chance to call completion routines, schedule, and whatever
  1754.     work.headerSize = sizeof(ComponentMPWorkFunctionHeaderRecord);
  1755.     work.recordSize = sizeof(ComponentMPWorkFunctionHeaderRecord);
  1756.     work.isRunning = false;
  1757.     work.workFlags = mpWorkFlagGetIsRunning;
  1758.  
  1759.     if (glob->decompressFunction)
  1760.         CallComponentMPWorkFunctionProc(glob->decompressFunction, glob->decompressFunctionRefCon, &work);
  1761.  
  1762.     return glob->decompressCount != 0;
  1763. }
  1764.  
  1765. #endif
  1766.