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
Connecting to the Base Image Decompressor
Providing Storage for Frame Decompression
Initializing Your Decompressor Component
Specifying Other Capabilities of Your Component
Implementing Functions for Queues
Decompressing Bands
Implementing ImageCodecBeginBand
Implementing ImageCodecDrawBand
Implementing ImageCodecEndBand
Providing Information About the Decompressor
Providing Progress Information
Handling and Delegating Other Calls
Closing the Component
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.
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); |
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.
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:
canAsync
specifies whether the component can support asynchronous decompression operations, as described in Asynchronous Decompression.
decompressRecordSize
specifies the size of the ImageSubCodecDecompressRecord
structure that the base decompressor component creates for your image decompressor component.
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; |
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); |
} |
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.
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.
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; |
} |
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; |
} |
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.
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.
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.
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:
codecProgressOpen
codecProgressClose
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.
When your image decompressor component closes, it must close its connection to the base image decompressor by calling the CloseComponent
function.
© 2005, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-01-10)