About the Base Image Decompressor

This chapter describes the features of the base image decompressor. The base image decompressor is an Apple-supplied component that makes it easier for developers to create new decompressors. The base image decompressor does most of the housekeeping and interface functions required for a QuickTime decompressor component, including scheduling for asynchronous decompression.

Whenever possible, an image decompressor component should handle asynchronous requests for decompression, as described in Asynchronous Decompression. If you are implementing an image decompressor component, you can include this capability with a minimum of additional programming by using the services of the base image decompressor. The base image decompressor handles the necessary scheduling, which frees you to concentrate on the details of decompression.

When you use the base image decompressor with an image decompressor component, your component must support functions that are called by the base image decompressor when necessary. Your component can then delegate a number of other function calls to the base image decompressor, which greatly simplifies the implementation of your component.

Using the Base Image Decompressor

To use the services of the base image decompressor, your image decompressor component must support functions that the base image decompressor calls when necessary. The following sections explain when the base image decompressor calls these functions and how your image decompressor component must respond. These sections also list standard image decompressor calls that your image decompressor must handle itself rather than delegate.

The base image decompressor and image decompressor components are managed by the Component Manager.

Connecting to the Base Image Decompressor

To use the services of the base image decompressor, your image decompressor component must open a connection to the base image decompressor component. Listing 11-1 illustrates how to make the connection.

Listing 11-1  Connecting to the base image decompressor component

ComponentInstance baseCodec;
OSErr err;
err = OpenADefaultComponent (decompressorComponentType,
                             kBaseCodecType,
                             &baseCodec);
err = ComponentSetTarget (baseCodec,
                          self);

Providing Storage for Frame Decompression

Your image decompressor component uses an ImageSubCodecDecompressRecord structure to store information needed to decompress a single frame. The structure is created by the base decompressor component when your component is initialized, as described in Initializing Your Decompressor Component.

Initializing Your Decompressor Component

The first function call that your image decompressor component receives from the base image decompressor is always a call to ImageCodecInitialize. In response to this call, your image decompressor component returns an ImageSubCodecDecompressCapabilities structure that specifies its capabilities. This structure contains the following fields:

With the help of the base image decompressor, any image decompressor that uses only interrupt-safe calls for decompression operations can support asynchronous decompression.

Listing 11-2 shows how to specify that a decompressor supports asynchronous decompression operations.

Listing 11-2  Specifying the capabilities of a decompressor component.

ImageSubCodecDecompressCapabilities deccap;
ImageSubCodecDecompressRecord decrec;
deccap->decompressRecordSize = sizeof(decrec);
deccap->canAsync = true;

Specifying Other Capabilities of Your Component

The base image decompressor gets additional information about the capabilities of your image decompressor component by calling your component’s ImageCodecPreflight function. The base image decompressor uses this information when responding to a call to the ImageCodecPredecompress function, which the Image Compression Manager makes before decompressing an image.

Your image decompressor component returns information about its capabilities by filling in the capabilities structure. Listing 11-3 illustrates how to fill in this structure. In this example, the decompressor component specifies that it supports the ARGB, ABGR, BGRA, and RGBA pixel formats used by Microsoft Windows.

Listing 11-3  Sample implementation of ImageCodecPreflight

pascal ComponentResult ImageCodecPreflight (
                         ComponentInstance ci,
                         CodecDecompressParams *p)
{
    register CodecCapabilities*capabilities = p->capabilities;
    /*  Decide which depth compressed data we can deal with. */
    
    switch ( (*p->imageDescription)->depth ) {
        case 16:
            break;
        default:
            return(codecConditionErr);
            break;
    }
    
    /*  We can deal only 32 bit pixels. */
    capabilities->wantedPixelSize = 32;
    
    /*  The smallest possible band we can do is 2 scan lines. */
    
    capabilities->bandMin = 2;
    /*  We can deal with 2 scan line high bands. */
    capabilities->bandInc = 2;
    
    /*  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.
     */
    capabilities->extendWidth = p->srcRect.right & 1;
    capabilities->extendHeight = p->srcRect.bottom & 1;
    {
        OSType *pf = *glob->wantedDestinationPixelTypeH;
        p->wantedDestinationPixelTypes =
            glob->wantedDestinationPixelTypeH;
        // set up default order
        pf[0] = k32BGRAPixelFormat;
        pf[1] = k32ARGBPixelFormat;
        pf[2] = k32ABGRPixelFormat;
        pf[3] = k32RGBAPixelFormat;
        switch (p->dstPixMap.pixelFormat) {
        case k32BGRAPixelFormat: // we know how to do these pixel formats
            break;
        case k32ABGRPixelFormat:
            pf[0] = k32ABGRPixelFormat;
            pf[1] = k32BGRAPixelFormat;
            pf[2] = k32ARGBPixelFormat;
            pf[3] = k32RGBAPixelFormat;
            break;
        case k32ARGBPixelFormat:
            pf[0] = k32ARGBPixelFormat;
            pf[1] = k32BGRAPixelFormat;
            pf[2] = k32ABGRPixelFormat;
            pf[3] = k32RGBAPixelFormat;
            break;
        case k32RGBAPixelFormat:
            pf[0] = k32RGBAPixelFormat;
            pf[1] = k32BGRAPixelFormat;
            pf[2] = k32ARGBPixelFormat;
            pf[3] = k32ABGRPixelFormat;
            break;
        default:            // we don't know how to do these, so return
                            // the default
            break;
        }
    }
    return(noErr);
}

Implementing Functions for Queues

If the image decompressor component supports asynchronous scheduled decompression, it receives a ImageCodecQueueStarting call from the base image decompressor when processing of the queue begins and the ImageCodecQueueStopping function when processing of the queue is finished. It is not necessary for your image decompressor component to implement these functions. Implement them only if there are tasks that your image decompressor component must perform after being notified, such as locking structures in memory before ImageCodecDrawBand is called.

Calls to ImageCodecQueueStarting and ImageCodecQueueStopping are never made during interrupt time.

Decompressing Bands

Your image decompressor component must implement the ImageCodecBeginBand and ImageCodecDrawBand functions for decompressing bands. It can also implement the ImageCodecEndBand function to be information that decompression of a band is complete. It receives these calls from the base image decompressor when decompression of either a complete frame or an individual band needs to be performed.

Implementing ImageCodecBeginBand

The ImageCodecBeginBand function allows your image decompressor component to save information about a band before decompressing it. For example, your image decompressor component can change the value of the codecData pointer if not all of the data for the band needs to be decompressed. The base image decompressor preserves any changes your image decompressor component makes to any of the fields in the ImageSubCodecDecompressRecord or CodecDecompressParams structures.

The ImageCodecBeginBand function is never called at interrupt time. If your component supports asynchronous scheduled decompression, it may receive more than one ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.

A sample implementation of ImageCodecBeginBand is shown in Listing 11-4.

Listing 11-4  Sample implementation of ImageCodecBeginBand

pascal ComponentResult ImageCodecBeginBand (
                          ComponentInstance ci,
                          CodecDecompressParams *p,
                          ImageSubCodecDecompressRecord *drp,
                          long flags)
{
    ExampleDecompressRecord *mydrp = drp->userDecompressRecord;
    long            numLines,numStrips;
    long            stripBytes;
    short           width;
    short           y;
    OSErr           result = noErr;
    Ptr             cDataPtr;
    
    /* initialize some local variables */
    
    width = (*p->imageDescription)->width;
    numLines = p->stopLine - p->startLine;  /* number of scanlines in */
                                            /* this band */ 
    numStrips = (numLines+1)>>1;    /* number of strips in this band */
    stripBytes = ((width+1)>>1) * 5;    /* number of bytes in one */
                                        /* strip of blocks */ 
    cDataPtr = drp->codecData;
    
    /*
     *  If skipping some data, 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) && p->startLine != 0 ) {
        if ( p->dataProcRecord.dataProc ) {
            for ( y=0; y < p->startLine>>1; y++ ) {
                if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes,
                        drp->dataProcRecord.dataRefCon)) != noErr ) {
                    result = codecSpoolErr;
                    goto bail;
                }
                cDataPtr += stripBytes;
            }
        } else
            cDataPtr += (p->startLine>>1) * stripBytes;
    }
    
    drp->codecData = cDataPtr;
    mydrp->width = width;
    mydrp->numStrips = numStrips;
    mydrp->srcDataIncrement = stripBytes;
    mydrp->baseAddrIncrement = drp->rowBytes<<1;
    mydrp->glob = (void *)storage;
    /* figure out our dest pixel format and select the
       correct DecompressStripProc */
    switch(p->dstPixMap.pixelFormat) {
        case 0:     // old case where planebytes
                    // is not set by codecmanager
        case k32ARGBPixelFormat:
            mydrp->decompressStripProc = DecompressStrip32ARGB;
            break;
        case k32ABGRPixelFormat:
            mydrp->decompressStripProc = DecompressStrip32ABGR;
            break;
        case k32BGRAPixelFormat:
            mydrp->decompressStripProc = DecompressStrip32BGRA;
            break;
        case k32RGBAPixelFormat:
            mydrp->decompressStripProc = DecompressStrip32RGBA;
            break;
        default:
            bail;
            break;
    }
bail:
    return result;
}

Implementing ImageCodecDrawBand

When the base image decompressor calls your image decompressor component’s ImageCodecDrawBand function, your component must perform the decompression specified by the fields of the ImageSubCodecDecompressRecord structure. The structure includes any changes your component made to it when performing the ImageCodecBeginBand function.

If the ImageSubCodecDecompressRecord structure specifies either a progress function or a data-loading function, the base image decompressor never calls the ImageCodecDrawBand function at interrupt time. If not, the base image decompressor may call the ImageCodecDrawBand function at interrupt time.

If the ImageSubCodecDecompressRecord structure specifies a progress function, the base image decompressor handles codecProgressOpen and codecProgressClose calls, and your image decompressor component must not implement these functions.

If your component supports asynchronous scheduled decompression, it may receive more than one ImageCodecBeginBand call before receiving an ImageCodecDrawBand call.

A sample implementation of ImageCodecDrawBand is shown in Listing 11-5.

Listing 11-5  Sample implementation of ImageCodecDrawBand

pascal ComponentResult ImageCodecDrawBand (
                          ComponentInstance ci,
                          ImageSubCodecDecompressRecord *drp)
{
    ExampleDecompressRecord *mydrp = drp->userDecompressRecord;
    short y;
    Ptr cDataPtr = drp->codecData;  // compressed data pointer;
    Ptr baseAddr = drp->baseAddr;   // base address of dest PixMap;
    SInt8 mmuMode = true32b;        // we want to be in 32-bit mode
    OSErr err = noErr;
    for (y = 0; y < mydrp->numStrips; y++) {
        if (drp->dataProcRecord.dataProc) {
            if ( (err =
CallICMDataProc(drp->dataProcRecord.dataProc,&cDataPtr,
                        mydrp->srcDataIncrement,
                drp->dataProcRecord.dataRefCon)) != noErr ) {
                err = codecSpoolErr;
                goto bail;
            }
        }
        SwapMMUMode(&mmuMode);      // put us in 32-bit mode
(mydrp->decompressStripProc)(cDataPtr,baseAddr,(short)drp->rowBytes,
                            (short)mydrp->width,glob->sharedGlob); 
        SwapMMUMode(&mmuMode);      // put us back
        baseAddr += mydrp->baseAddrIncrement;
        cDataPtr += mydrp->srcDataIncrement;
        if (drp->progressProcRecord.progressProc) {
            if ( (err =
CallICMProgressProc(drp->progressProcRecord.progressProc,
                codecProgressUpdatePercent,
                FixDiv ( y, mydrp->numStrips),
                drp->progressProcRecord.progressRefCon)) != noErr ) {
                err = codecAbortErr;
                goto bail;
            }
        }
    }
bail:
    return err;
}

Implementing ImageCodecEndBand

Your image decompressor component is not required to implement the ImageCodecEndBand function. If it does, the base image decompressor calls the function when the decompression of a band is complete or is terminated by the Image Compression Manager. The call simply notifies your component that decompression is finished. After your component handles the call, it can perform any tasks that are necessary when decompression is finished, such as disposing of data structures that are no longer used, after receiving notification. Note that because the ImageCodecEndBand function can be called at interrupt time, your image decompressor component cannot use this function to dispose of data structures; this must occur after handling the function.

Providing Information About the Decompressor

Your image decompressor component must also implement the ImageCodecGetCodecInfo. This performs the same task as the CDGetCodecInfo function. The Image Compression Manager calls your image decompressor component’s ImageCodecGetCodecInfo function when it receives a GetCodecInfo call.

Providing Progress Information

If the ImageSubCodecDecompressRecord structure does not specify a progress function, your image decompressor component can implement codecProgressOpen and codecProgressClose functions to provide progress information. If the ImageSubCodecDecompressRecord structure does specify a progress function, your image decompressor component can implement the codecProgressUpdatePercent function to provide progress information during lengthy decompression operations. Implementing this function is optional.

Handling and Delegating Other Calls

Your image decompressor component must delegate the following image decompressor component calls to the base image decompressor:

If the ImageSubCodecDecompressRecord structure specifies a progress function, your image decompressor component must also delegate these decompressor component calls to the base image decompressor:

Your image decompressor component can implement any other image decompressor component functions itself or delegate any of the calls to the base image decompressor. To delegate calls, it uses the DelegateComponentCall function.

Closing the Component

When your image decompressor component closes, it must close its connection to the base image decompressor by calling the CloseComponent function.