Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
examplecodec.c
/* |
File: examplecodec.c |
Contains: This is an example of am image compression codec that handles both |
compression and decompression of images as passed to it by the |
Image Compression manager. It is built as a Component Manager Component. |
The compression scheme here is 411 YUV. The image is stored as separate |
luminance and chrominance channels. For each 2x2 block of pixels in the |
source image we store 4 luminance (Y) components, 1 Y-Red component (U) and |
1 Y-Blue (V) component. Each Y-component is stored as 6-bits, resulting in a |
savings of 2.4:1 over a 24-bit/pixel image (6*4 + 2*8)/4 = 10 bits/pixel. |
Written by: |
Copyright: Copyright © 1999 by Apple Computer, Inc., All Rights Reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
7/29/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <Memory.h> |
#include <Resources.h> |
#include <QuickDraw.h> |
#include <QDOffscreen.h> |
#include <OSUtils.h> |
#include <Errors.h> |
#include <FixMath.h> |
#include "ImageCodec.h" |
/* Version information */ |
#define EXAMPLE_CODEC_REV 1 |
#define codecInterfaceVersion 1 /* high word returned in component GetVersion */ |
/* Some useful macros and constants */ |
#define R_W 0x4ccd |
#define G_W 0x970a |
#define B_W 0x1c29 |
#define PIN(_n) ((_n) < 0 ? 0 : (_n) > 255 ? 255 : (_n)) |
/* |
Our data structure declarations |
*/ |
/* This is the structure we use to hold data used by all instances of |
this compressor and decompressor */ |
typedef struct { |
Handle rgbwTable; /* optional encode/decode table */ |
Handle giwTable; /* another optional encode/decode table */ |
CodecInfo **info; /* our cached codec info structure */ |
} SharedGlobals; |
/* This is the structure we use to store our global data for each instance */ |
typedef struct { |
SharedGlobals *sharedGlob; /* pointer to instance-shared globals */ |
} Globals; |
/* Function prototypes to keep the compiler smiling. */ |
ComponentResult |
InitSharedTables(Globals **glob,ComponentInstance self); |
pascal ComponentResult |
EXAMPLECODEC(ComponentParameters *params,char **storage); |
pascal ComponentResult |
OpenCodec(ComponentInstance self); |
pascal ComponentResult |
CloseCodec(Handle storage,ComponentInstance self); |
pascal ComponentResult |
CanDoSelector(short selector); |
pascal ComponentResult |
GetVersion(); |
pascal void |
CompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg); |
pascal void |
DecompressStrip(char *data,char *baseAddr,short rowBytes,short w,SharedGlobals *sg); |
pascal long |
CDPreCompress(Handle storage,register CodecCompressParams *p); |
pascal long |
CDBandCompress(Handle storage,register CodecCompressParams *p); |
pascal long |
CDPreDecompress(Handle storage,register CodecDecompressParams *p); |
pascal long |
CDBandDecompress(Handle storage,register CodecDecompressParams *p); |
pascal ComponentResult |
CDGetCodecInfo(Handle storage,CodecInfo *info); |
pascal ComponentResult |
CDGetSimilarity(Handle storage,PixMapHandle src,const Rect *srcRect,ImageDescriptionHandle desc, |
Ptr data,Fixed *similarity); |
pascal ComponentResult |
CDGetCompressedImageSize(Handle storage,ImageDescriptionHandle desc,Ptr data,long dataSize, |
ICMDataProcRecordPtr dataProc,long *size); |
pascal ComponentResult |
CDGetMaxCompressionSize(Handle storage,PixMapHandle src,const Rect *srcRect,short depth, |
CodecQ quality,long *size); |
pascal ComponentResult |
CDGetCompressionTime(Handle storage,PixMapHandle src,const Rect *srcRect,short depth, |
CodecQ *spatialQuality,CodecQ *temporalQuality,unsigned long *time); |
pascal ComponentResult |
CDTrimImage(Handle storage,ImageDescriptionHandle desc,Ptr inData,long inDataSize, |
ICMDataProcRecordPtr dataProc,Ptr outData,long outDataSize,ICMFlushProcRecordPtr flushProc, |
Rect *trimRect,ICMProgressProcRecordPtr progressProc); |
pascal void |
CompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg); |
/************************************************************************************ |
* This is the main dispatcher for our codec. All calls from the codec manager |
* will come through here, with a unique selector and corresponding parameter block. |
* |
* This routine must be first in the code segment of the codec thing. |
*/ |
pascal ComponentResult |
EXAMPLECODEC(ComponentParameters *params,char **storage) |
{ |
/* If the selector is less than zero, it's a Component manager selector. */ |
if ( params->what < 0 ) { |
switch ( params->what ) { |
case kComponentOpenSelect: |
return CallComponentFunction(params, (ComponentFunctionUPP) OpenCodec ); |
case kComponentCloseSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP) CloseCodec ); |
case kComponentCanDoSelect: |
return CallComponentFunction(params, (ComponentFunctionUPP) CanDoSelector); |
case kComponentVersionSelect : |
return CallComponentFunction(params, (ComponentFunctionUPP) GetVersion); |
default : |
return (paramErr); |
} |
} |
/* |
* Here we dispatch the rest of our calls. We use the magic thing manager routine which |
* calls our subroutines with the proper parameters. The prototypes are in Image Codec.h. |
*/ |
switch ( params->what ) { |
case kImageCodecPreCompressSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDPreCompress); |
case kImageCodecBandCompressSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDBandCompress); |
case kImageCodecPreDecompressSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDPreDecompress); |
case kImageCodecBandDecompressSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDBandDecompress); |
case kImageCodecBusySelect: |
return 0; /* our codec is never asynchronously busy */ |
case kImageCodecGetCodecInfoSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCodecInfo); |
case kImageCodecGetCompressedImageSizeSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCompressedImageSize); |
case kImageCodecGetMaxCompressionSizeSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetMaxCompressionSize); |
case kImageCodecGetCompressionTimeSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetCompressionTime); |
case kImageCodecGetSimilaritySelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDGetSimilarity); |
case kImageCodecTrimImageSelect: |
return CallComponentFunctionWithStorage(storage,params,(ComponentFunctionUPP)CDTrimImage); |
default: |
return(paramErr); |
} |
} |
/************************************************************************************ |
* This gets called when the thing instance is opened. We allocate our storage at this |
* point. If we have shared globals, we check if they exist, and put a pointer to them |
* in our instance globals so that other calls can get to them. |
*/ |
pascal ComponentResult |
OpenCodec(ComponentInstance self) |
{ |
ComponentResult result; |
Globals **glob; |
/* |
First we allocate shared storage. This should be a handle to any |
kind of data used by the thing instance. They should be allocated |
in the current heap. |
*/ |
if ( (glob = (Globals **)NewHandleClear(sizeof(Globals))) == nil ) { |
return(MemError()); |
} |
SetComponentInstanceStorage(self,(Handle)glob); |
/* Check and initialize our shared globals */ |
result = InitSharedTables(glob,self); |
return result; |
} |
/************************************************************************************ |
* This gets called when the thing instance is opened. We allocate our shared storage at this |
* point. |
* If we have shared globals, we check if they exist, otherwise we allocate |
* them and set the ComponentRefCon so that other instances can use them. |
* |
* The shared globals hold our CodecInfo struct, which we read from our resource file, |
* and some tables that we use for speed. If we cant get the tables we can work without |
* them. All items in the shared globals are made purgeable when the last of our |
* instances is closed. If our component was loaded in the application heap ( because |
* there was no room in the system heap) then we keep our shared storage in the app heap. |
* |
* We keep a pointer to the shared globals in our instance globals so that other calls can get to them. |
*/ |
ComponentResult |
InitSharedTables(Globals **glob,ComponentInstance self) |
{ |
SharedGlobals *sGlob; |
long i,j,*lp; |
char *cp; |
short resFile; |
THz saveZone; |
Boolean inAppHeap; |
OSErr result = noErr; |
saveZone = GetZone(); |
inAppHeap = ( GetComponentInstanceA5(self) != 0 ); |
if ( !inAppHeap ) |
SetZone(SystemZone()); |
if ( (sGlob=(SharedGlobals*)GetComponentRefcon((Component)self)) == nil ) { |
if ( (sGlob = (SharedGlobals*)NewPtrClear(sizeof(SharedGlobals))) == nil ) { |
result = MemError(); |
goto obail; |
} |
SetComponentRefcon((Component)self,(long)sGlob); |
} |
(*glob)->sharedGlob = sGlob; // keep this around where it's easy to get at |
if ( sGlob->info == nil || *(Handle)sGlob->info == nil ) { |
if ( sGlob->info ) |
DisposeHandle((Handle)sGlob->info); |
/* Get the CodecInfo struct which we keep in our resource fork */ |
resFile = OpenComponentResFile((Component)self); |
if (resFile == -1) { |
result = memFullErr; |
goto obail; |
} |
sGlob->info = (CodecInfo **) Get1Resource(codecInfoResourceType,128); |
if ( sGlob->info == nil ) { |
CloseComponentResFile(resFile); |
result = ResError(); |
goto obail; |
} |
LoadResource((Handle)sGlob->info); |
if ( ResError() ) { |
CloseComponentResFile(resFile); |
result = ResError(); |
goto obail; |
} |
DetachResource((Handle)sGlob->info); |
CloseComponentResFile(resFile); |
} |
HNoPurge((Handle)sGlob->info); |
if ( sGlob->rgbwTable == nil || *sGlob->rgbwTable == nil ) { |
if ( sGlob->rgbwTable ) |
ReallocateHandle(sGlob->rgbwTable,3*256*sizeof(long)); |
else |
sGlob->rgbwTable = NewHandleSys(3*256*sizeof(long)); |
/* we can actual still work without these tables, so we dont bail |
if we cant get the memory for them */ |
if ( sGlob->rgbwTable && *sGlob->rgbwTable ) { |
lp = (long *)*sGlob->rgbwTable; |
for ( i=0, j = 0; i < 256; i++, j += R_W ) |
*lp++ = j; |
for ( i=0, j = 0; i < 256; i++, j += G_W ) |
*lp++ = j; |
for ( i=0, j = 0; i < 256; i++, j += B_W ) |
*lp++ = j; |
} |
} |
if ( sGlob->rgbwTable ) |
HNoPurge(sGlob->rgbwTable); /* make sure it wont get purged while we are open */ |
/* green inverse table */ |
if ( sGlob->giwTable == nil || *sGlob->giwTable == nil ) { |
if ( sGlob->giwTable ) |
ReallocateHandle(sGlob->giwTable,256); |
else |
sGlob->giwTable = NewHandleSys(256); |
/* we can actual still work without these tables, so we dont bail |
if we cant get hte memory for them */ |
if ( sGlob->giwTable && *sGlob->giwTable ) { |
for ( i=0, cp = *sGlob->giwTable ; i < 256; i++ ) |
*cp++ = PIN( (i<<16) / G_W); |
} |
} |
if ( sGlob->giwTable ) |
HNoPurge(sGlob->giwTable); /* make sure it wont get purged while we are open */ |
obail: |
SetZone(saveZone); |
if ( result != noErr && sGlob != nil ) { |
if ( sGlob->rgbwTable ) |
DisposeHandle(sGlob->rgbwTable); |
if ( sGlob->info ) |
DisposeHandle((Handle)sGlob->info); |
DisposePtr((Ptr)sGlob); |
SetComponentRefcon((Component)self,(long)nil); |
} |
return(result); |
} |
/************************************************************************************ |
* This gets called when the thing instance is closed. We need to get rid of any |
* instance storage here. |
*/ |
pascal ComponentResult |
CloseCodec(Handle storage,ComponentInstance self) |
{ |
SharedGlobals *sGlob; |
Globals **glob = (Globals **)storage; |
/* If we are closing our last instance |
then we make the shared global items purgeable to |
speed things up next instance. |
*/ |
if ( CountComponentInstances((Component)self) == 1) { |
if ( (sGlob=(SharedGlobals*)(*glob)->sharedGlob) != nil ) { |
if ( sGlob->rgbwTable ) |
HPurge(sGlob->rgbwTable); |
if ( sGlob->giwTable ) |
HPurge(sGlob->giwTable); |
if ( sGlob->info ) |
HPurge((Handle)sGlob->info); |
} |
} |
if ( glob ) |
DisposeHandle((Handle)glob); |
return(noErr); |
} |
/************************************************************************************ |
* Return true if we can handle the selector, otherwise false. |
*/ |
pascal ComponentResult |
CanDoSelector(short selector) |
{ |
switch(selector) { |
case kComponentOpenSelect: |
case kComponentCloseSelect: |
case kComponentCanDoSelect: |
case kComponentVersionSelect : |
case kImageCodecPreCompressSelect: |
case kImageCodecBandCompressSelect: |
case kImageCodecPreDecompressSelect: |
case kImageCodecBandDecompressSelect: |
case kImageCodecBusySelect: |
case kImageCodecGetCodecInfoSelect: |
case kImageCodecGetCompressedImageSizeSelect: |
case kImageCodecGetMaxCompressionSizeSelect: |
case kImageCodecGetCompressionTimeSelect: |
case kImageCodecGetSimilaritySelect: |
case kImageCodecTrimImageSelect: |
return(true); |
default: |
return (false); |
} |
} |
/************************************************************************************ |
* Return the version of this component ( defines interface ) and revision level |
* of the code. |
*/ |
pascal ComponentResult |
GetVersion() |
{ |
return ((codecInterfaceVersion<<16) | EXAMPLE_CODEC_REV); /* interface version in hi word, code rev in lo word */ |
} |
/************************************************************************************ |
* CDPreCompress gets called before an image is compressed, or whenever the source pixmap |
* pixel size changes when compressing a sequence. We return information about |
* how we can compress the image to the codec manager, so that it can fit the source data |
* to our requirements. The ImageDescriptor is already filled in, so we can use it for |
* reference (or even add to it ). PixelSize is the pixel depth of the source pixmap, we |
* use this as a reference for deciding what we can do. The other parameters return information |
* to the CodecManager about what we can do. We can also do setup here if we want to. |
*/ |
pascal long |
CDPreCompress(Handle storage,register CodecCompressParams *p) |
{ |
#pragma unused(storage) |
CodecCapabilities *capabilities = p->capabilities; |
/* |
* First we return which depth input pixels we can deal with - based on what the |
* app has available - we can only work with 32 bit input pixels. |
*/ |
switch ( (*p->imageDescription)->depth ) { |
case 16: |
capabilities->wantedPixelSize = 32; |
break; |
default: |
return(codecConditionErr); |
break; |
} |
/* if the buffer gets banded, return the smallest one we can deal with */ |
capabilities->bandMin = 2; |
/* if the buffer gets banded, return the increment it be should grown */ |
capabilities->bandInc = 2; |
/* |
* If a codec needs the dimensions of the source pixmap to be of certain multiples |
* it can ask for the image to be extended out (via pixel replication) vertically |
* and/or horizontally. |
* |
* In our case, we're dealing with 2 by 2 blocks and therefore we want the image |
* height and width to both be multiples of 2. If either dimension is odd, we |
* ask it have it extended by one pixel. |
*/ |
capabilities->extendWidth = (*p->imageDescription)->width & 1; |
capabilities->extendHeight = (*p->imageDescription)->height & 1; |
return(noErr); |
} |
/************************************************************************************ |
* CDBandCompress gets called when the codec manager wants us to compress an image, or a horizontal |
* band of an image. The pixel data at sBaseAddr is guaranteed to conform to the criteria we |
* demanded in BeginCompress. |
*/ |
pascal long |
CDBandCompress(Handle storage,register CodecCompressParams *p) |
{ |
short width,height; |
Ptr cDataPtr,dataStart; |
short depth; |
Rect sRect; |
long offsetH,offsetV; |
Globals **glob = (Globals **)storage; |
register char *baseAddr; |
long numLines,numStrips; |
short rowBytes; |
long stripBytes; |
char mmuMode = 1; |
register short y; |
ImageDescription **desc = p->imageDescription; |
OSErr result = noErr; |
/* If there is a progress proc, give it an open call at the start of this band. */ |
if (p->progressProcRecord.progressProc) |
CallICMDataProc(p->progressProcRecord.progressProc,codecProgressOpen,0, |
p->progressProcRecord.progressRefCon); |
width = (*desc)->width; |
height = (*desc)->height; |
depth = (*desc)->depth; |
dataStart = cDataPtr = p->data; |
/* figure out offset to first pixel in baseAddr from the pixelsize and bounds */ |
rowBytes = p->srcPixMap.rowBytes & 0x3fff; |
sRect = p->srcPixMap.bounds; |
numLines = p->stopLine - p->startLine; /* number of scanlines in this band */ |
numStrips = (numLines+1)>>1; /* number of strips in this band */ |
stripBytes = ((width+1)>>1) * 5; |
/* adjust the source baseAddress to be at the beginning of the desired rect */ |
switch ( p->srcPixMap.pixelSize ) { |
case 32: |
offsetH = sRect.left<<2; |
break; |
case 16: |
offsetH = sRect.left<<1; |
break; |
case 8: |
offsetH = sRect.left; |
break; |
default: |
result = codecErr; |
goto bail; |
} |
offsetV = sRect.top * rowBytes; |
baseAddr = p->srcPixMap.baseAddr + offsetH + offsetV; |
/* if there is not a flush proc, adjust the pointer to the next band */ |
if ( p->flushProcRecord.flushProc == nil ) |
cDataPtr += (p->startLine>>1) * stripBytes; |
else { |
if ( p->bufferSize < stripBytes ) { |
result = codecSpoolErr; |
goto bail; |
} |
} |
/* do the slower flush/progress case if we have too */ |
if ( p->flushProcRecord.flushProc || p->progressProcRecord.progressProc ) { |
SharedGlobals *sg = (*glob)->sharedGlob; |
for ( y=0; y < numStrips; y++) { |
SwapMMUMode(&mmuMode); |
CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg); |
SwapMMUMode(&mmuMode); |
baseAddr += rowBytes<<1; |
if ( p->flushProcRecord.flushProc ) { |
if (( result=CallICMFlushProc(p->flushProcRecord.flushProc,cDataPtr,stripBytes, |
p->flushProcRecord.flushRefCon)) != noErr) { |
result = codecSpoolErr; |
goto bail; |
} |
} else { |
cDataPtr += stripBytes; |
} |
if (p->progressProcRecord.progressProc) { |
if(( result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent, |
FixDiv(y,numStrips),p->progressProcRecord.progressRefCon)) != noErr ) { |
result = codecAbortErr; |
goto bail; |
} |
} |
} |
} else { |
SharedGlobals *sg = (*glob)->sharedGlob; |
short tRowBytes = rowBytes<<1; |
SwapMMUMode(&mmuMode); |
for ( y=numStrips; y--; ) { |
CompressStrip(cDataPtr,baseAddr,rowBytes,width,sg); |
cDataPtr += stripBytes; |
baseAddr += tRowBytes; |
} |
SwapMMUMode(&mmuMode); |
} |
/* |
return size and similarity on the last band |
*/ |
if ( p->conditionFlags & codecConditionLastBand ) { |
(*p->imageDescription)->dataSize = stripBytes * ((height+1)>>1); /* return the actual size of the compressed data */ |
p->similarity = 0; /* we don't do frame differencing */ |
} |
bail: |
/* If there is a progress proc, give it a close call at the end of this band. */ |
if (p->progressProcRecord.progressProc) |
CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0, |
p->progressProcRecord.progressRefCon); |
return(result); |
} |
/************************************************************************************ |
* CDPreDecompress gets called before an image is decompressed. We return information about |
* how we can decompress the image to the codec manager, so that it can fit the destination data |
* to our requirements. |
*/ |
pascal long |
CDPreDecompress(Handle storage,register CodecDecompressParams *p) |
{ |
#pragma unused(storage) |
register CodecCapabilities *capabilities = p->capabilities; |
Rect dRect = p->srcRect; |
/* Check if the matrix is okay for us. We don't do anything fancy. */ |
if ( !TransformRect(p->matrix,&dRect,nil) ) |
return(codecConditionErr); |
/* Decide which depth compressed data we can deal with. */ |
switch ( (*p->imageDescription)->depth ) { |
case 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; |
return(noErr); |
} |
/************************************************************************************ |
* CDBandDecompress gets called when the codec manager wants us to decompress an image or a horizontal |
* band of an image. The pixel data at baseAddr is guaranteed to conform to the criteria we |
* demanded in BeginDecompress. If maskIn is true, then the mask data at mBaseAddr is valid, and |
* we need to clear bits in it that correspond to any pixels in the destination we do not want to |
* change. ( We always write all pixels, so we dont care. This mode is important only for those |
* codecs that have frame differencing and don't always write all the pixels. ) |
*/ |
pascal long |
CDBandDecompress(Handle storage,register CodecDecompressParams *p) |
{ |
Rect dRect; |
long offsetH,offsetV; |
Globals **glob = (Globals **)storage; |
long numLines,numStrips; |
short rowBytes; |
long stripBytes; |
short width; |
register short y; |
register char *baseAddr; |
char *cDataPtr; |
char mmuMode = 1; |
OSErr result = noErr; |
/* Calculate the real base address based on the bounds rect. If it's not |
a linear transformation, we dont do it. */ |
dRect = p->srcRect; |
if ( !TransformRect(p->matrix,&dRect,nil) ) |
return(paramErr); |
/* If there is a progress proc, give it an open call at the start of this band. */ |
if (p->progressProcRecord.progressProc) |
CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressOpen,0, |
p->progressProcRecord.progressRefCon); |
/* initialize some local variables */ |
width = (*p->imageDescription)->width; |
rowBytes = p->dstPixMap.rowBytes; |
numLines = p->stopLine - p->startLine; /* number of scanlines in this band */ |
numStrips = (numLines+1)>>1; /* number of strips in this band */ |
stripBytes = ((width+1)>>1) * 5; /* number of bytes in one strip of blocks */ |
cDataPtr = p->data; |
/* adjust the destination baseaddress to be at the beginning of the desired rect */ |
offsetH = (dRect.left - p->dstPixMap.bounds.left); |
switch ( p->dstPixMap.pixelSize ) { |
case 32: |
offsetH <<=2; /* 1 pixel = 4 bytes */ |
break; |
case 16: |
offsetH <<=1; /* 1 pixel = 2 bytes */ |
break; |
case 8: |
break; /* 1 pixel = 1 byte */ |
default: |
result = codecErr; /* we dont handle these cases, thow we could */ |
goto bail; |
} |
offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes; |
baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV; |
/* |
* If we are skipping some data, we just skip it here. We can tell because |
* firstBandInFrame says this is the first band for a new frame, and |
* if startLine is not zero, then that many lines were clipped out. |
*/ |
if ( (p->conditionFlags & codecConditionFirstBand) && p->startLine != 0 ) { |
if ( p->dataProcRecord.dataProc ) { |
for ( y=0; y < p->startLine>>1; y++ ) { |
if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes, |
p->dataProcRecord.dataRefCon)) != noErr ) { |
result = codecSpoolErr; |
goto bail; |
} |
cDataPtr += stripBytes; |
} |
} else |
cDataPtr += (p->startLine>>1) * stripBytes; |
} |
/* |
* If theres a dataproc spooling the data to us, then we have to do the data |
* in whatever size chunks they want to give us, or if there is a progressProc |
* make sure to call it as we go along. |
*/ |
if ( p->dataProcRecord.dataProc || p->progressProcRecord.progressProc ) { |
SharedGlobals *sg = (*glob)->sharedGlob; |
for (y=0; y < numStrips; y++) { |
if (p->dataProcRecord.dataProc) { |
if ( (result=CallICMDataProc(p->dataProcRecord.dataProc,&cDataPtr,stripBytes, |
p->dataProcRecord.dataRefCon)) != noErr ) { |
result = codecSpoolErr; |
goto bail; |
} |
} |
SwapMMUMode(&mmuMode); |
DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg); |
SwapMMUMode(&mmuMode); |
baseAddr += rowBytes<<1; |
cDataPtr += stripBytes; |
if (p->progressProcRecord.progressProc) { |
if ( (result=CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressUpdatePercent, |
FixDiv(y, numStrips),p->progressProcRecord.progressRefCon)) != noErr ) { |
result = codecAbortErr; |
goto bail; |
} |
} |
} |
/* |
* |
* otherwise - do the fast case. |
* |
*/ |
} else { |
SharedGlobals *sg = (*glob)->sharedGlob; |
short tRowBytes = rowBytes<<1; |
SwapMMUMode(&mmuMode); |
for ( y=numStrips; y--; ) { |
DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg); |
baseAddr += tRowBytes; |
cDataPtr += stripBytes; |
} |
SwapMMUMode(&mmuMode); |
} |
/* |
* |
* IMPORTANT: update pointer to data in params, so when we get the next |
* band we'll be at the right place in our data. |
* |
*/ |
p->data = cDataPtr; |
if ( p->conditionFlags & codecConditionLastBand ) { |
/* Tie up any loose ends on the last band of the frame, if we had any */ |
} |
bail: |
/* If there is a progress proc, give it a close call at the end of this band. */ |
if (p->progressProcRecord.progressProc) |
CallICMProgressProc(p->progressProcRecord.progressProc,codecProgressClose,0, |
p->progressProcRecord.progressRefCon); |
return(result); |
} |
/************************************************************************************ |
* CDGetCodecInfo allows us to return information about ourselves to the codec manager. |
* |
* There will be a tool for determining appropriate values for the accuracy, speed |
* and level information. For now we estimate with scientific wild guessing. |
* |
* The info is stored as a resource in the same file with our component. |
*/ |
pascal ComponentResult |
CDGetCodecInfo(Handle storage,CodecInfo *info) |
{ |
Globals **glob = (Globals **)storage; |
if ( info == nil ) |
return(paramErr); |
BlockMove((Ptr)*((*glob)->sharedGlob)->info,(Ptr)info,sizeof(CodecInfo)); |
return(noErr); |
} |
/************************************************************************************ |
* When CDGetSimilarity is called, we return the percent of the compressed image A that |
* is different from compressed image B. This can be used by applications that use sequence |
* dynamics as an indicator for editing image sequences. |
* |
* If the codec cannot do a direct similarity comparison, the ICM decompresses image A and |
* do a comparison with image B. This call is provided so that a codec can save the time |
* of the intermediate decompress if it can do the comparison directly. |
*/ |
pascal ComponentResult |
CDGetSimilarity(Handle storage,PixMapHandle src,const Rect *srcRect,ImageDescriptionHandle desc, |
Ptr data,Fixed *similarity) |
{ |
#pragma unused(storage,src,srcRect,desc,data,similarity) |
/* This call is not implemented yet, which is okay, because its not used very much and is not mandatory */ |
return(codecUnimpErr); |
} |
/************************************************************************************ |
* When CDGetCompressedImageSize is called, we return the size in bytes of the given compressed |
* data ( for one image frame). |
*/ |
pascal ComponentResult |
CDGetCompressedImageSize(Handle storage,ImageDescriptionHandle desc,Ptr data,long dataSize, |
ICMDataProcRecordPtr dataProc,long *size) |
{ |
#pragma unused(storage,data,dataSize,dataProc) |
short width =(*desc)->width; |
short height = (*desc)->height; |
if ( size == nil ) |
return(paramErr); |
/* |
* Our data has a size which is deterministic based on the image size. If it were not we |
* could encode the size in the compressed data, or figure it out by walking the |
* compressed data. |
*/ |
*size = ((width+1)/2) * 5 * ((height+1)/2); |
return(noErr); |
} |
/************************************************************************************ |
* When CDGetMaxCompressionSize is called, we return the maximum size the compressed data for |
* the given image would be in bytes. |
*/ |
pascal ComponentResult |
CDGetMaxCompressionSize(Handle storage,PixMapHandle src,const Rect *srcRect,short depth, |
CodecQ quality,long *size) |
{ |
#pragma unused(storage,src,depth,quality) |
short width = srcRect->right - srcRect->left; |
short height = srcRect->bottom - srcRect->top; |
/* we always end up with a fixed size. If we did not, we would return the worst case size */ |
*size = ((width+1)/2) * 5 * ((height+1)/2); |
return(noErr); |
} |
/************************************************************************************ |
* When CDGetCompressionTime is called, we return the approximate time for compressing |
* the given image would be in milliseconds. We also return the closest actual quality |
* we can handle for the requested value. |
*/ |
pascal ComponentResult |
CDGetCompressionTime(Handle storage,PixMapHandle src,const Rect *srcRect,short depth, |
CodecQ *spatialQuality,CodecQ *temporalQuality,unsigned long *time) |
{ |
#pragma unused(storage,src,srcRect,depth) |
if (time) |
*time = 0; /* we don't know how many msecs */ |
if (spatialQuality) |
*spatialQuality = codecNormalQuality; /* we have only one quality level for now */ |
if (temporalQuality) |
*temporalQuality = 0; /* we cannot do temporal compression */ |
return(noErr); |
} |
/************************************************************************************ |
* When CDTrimImage is called, we take the given compressed data and return only the portion |
* which is represented by the trimRect. We can return a little more if we have too, but we |
* need only return enough so that the image in trimRect is properly displayed. We then |
* adjust the rectangle to corresond to the same rectangle in the new trimmed data. |
*/ |
pascal ComponentResult |
CDTrimImage(Handle storage,ImageDescriptionHandle desc,Ptr inData,long inDataSize, |
ICMDataProcRecordPtr dataProc,Ptr outData,long outDataSize,ICMFlushProcRecordPtr flushProc, |
Rect *trimRect,ICMProgressProcRecordPtr progressProc) |
{ |
#pragma unused(storage,inDataSize) |
Rect rect = *trimRect; |
char *dataP,*startP; |
short trimOffTop; |
short trimOffBottom; |
short trimOffLeft; |
short trimOffRight; |
short bytesOffLeft; |
short newHeight,newWidth; |
long size; |
short stripBytes; |
short newStripBytes; |
short y; |
OSErr result = noErr; |
if ( dataProc->dataProc == nil ) |
dataProc = nil; |
if ( flushProc->flushProc == nil ) |
flushProc = nil; |
if ( progressProc->progressProc == nil ) |
progressProc = nil; |
if ( progressProc ) |
CallICMProgressProc(progressProc->progressProc,codecProgressOpen,0,progressProc->progressRefCon); |
dataP = inData; |
newHeight = (*desc)->height; |
newWidth = (*desc)->width; |
stripBytes = ((newWidth+1)>>1) * 5; /* the number of bytes in a strip (2-scanlines/strip) */ |
/* figure out how many 2x2 blocks we want to strip from each side of the image */ |
trimOffTop = rect.top>>1; |
trimOffBottom = (newHeight - rect.bottom) >> 1; |
trimOffLeft = rect.left>>1; |
trimOffRight = (newWidth - rect.right) >> 1; |
/* point to the start of the first strip we are using */ |
startP = dataP + stripBytes * trimOffTop; |
/* make the trim values pixel based */ |
trimOffLeft <<= 1; |
trimOffTop <<= 1; |
trimOffBottom <<= 1; |
trimOffRight <<= 1; |
/* calculate new height and width */ |
newHeight -= trimOffTop + trimOffBottom; |
newWidth -= trimOffLeft + trimOffRight; |
/* calc size in bytes of strips of the new width */ |
newStripBytes = ((newWidth+1)>>1) * 5; |
/* figure number of bytes to toss at the beginning of each strip */ |
bytesOffLeft = (trimOffLeft>>1) * 5; |
/* figure size of new trimmed image */ |
size = newStripBytes * (newHeight>>1); |
/* make sure it's gonna fit */ |
if ( size > outDataSize ) { |
result = codecErr; |
goto bail; |
} |
/* now go through the strips and copy the needed portion of each to the new data */ |
if ( dataProc ) { |
short rightBytes = stripBytes - newStripBytes - bytesOffLeft; |
for ( y=0; y < trimOffTop; y++ ) { |
if ( (result=CallICMDataProc(dataProc->dataProc,&inData,stripBytes,dataProc->dataRefCon)) != noErr ) |
goto bail; |
inData += stripBytes; |
if (progressProc ) { |
if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent, |
FixDiv(y, (*desc)->height),progressProc->progressRefCon)) != noErr) { |
result = codecAbortErr; |
goto bail; |
} |
} |
} |
for ( y=0; y < newHeight; y+= 2) { |
if ( bytesOffLeft ) { |
if ( (result=CallICMDataProc(dataProc->dataProc,&inData,bytesOffLeft,dataProc->dataRefCon)) != noErr ) |
goto bail; |
inData += bytesOffLeft; |
} |
if ( (result=CallICMDataProc(dataProc->dataProc,&inData,newStripBytes,dataProc->dataRefCon)) != noErr ) |
goto bail; |
if ( flushProc ) { |
if ( (result=CallICMFlushProc(flushProc->flushProc,inData,newStripBytes,flushProc->flushRefCon)) != noErr ) { |
result = codecSpoolErr; |
goto bail; |
} |
} |
else { |
BlockMove(inData,outData,newStripBytes); |
outData += newStripBytes; |
} |
inData += newStripBytes; |
if ( rightBytes ) { |
if ( (result=CallICMDataProc(dataProc->dataProc,&inData,rightBytes,dataProc->dataRefCon)) != noErr ) { |
result = codecSpoolErr; |
goto bail; |
} |
inData += rightBytes; |
} |
if (progressProc) { |
if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent, |
FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr ) { |
result = codecAbortErr; |
goto bail; |
} |
} |
} |
} |
else { |
inData += stripBytes * trimOffTop; |
for ( y=0; y < newHeight; y += 2, inData += stripBytes) { |
if ( flushProc ) { |
if ( (result=CallICMFlushProc(flushProc->flushProc,inData + bytesOffLeft,newStripBytes,flushProc->flushRefCon)) != noErr ) { |
result = codecSpoolErr; |
goto bail; |
} |
} |
else { |
BlockMove(inData + bytesOffLeft,outData,newStripBytes); |
outData += newStripBytes; |
} |
if (progressProc ) { |
if ( (result=CallICMProgressProc(progressProc->progressProc,codecProgressUpdatePercent, |
FixDiv((trimOffTop + y),(*desc)->height),progressProc->progressRefCon)) != noErr ) { |
result = codecAbortErr; |
goto bail; |
} |
} |
} |
} |
/* adjust the rectangle to reflect our changes */ |
trimRect->top -= trimOffTop; |
trimRect->bottom -= trimOffTop; |
trimRect->left -= trimOffLeft; |
trimRect->right -= trimOffLeft; |
/* return the new width and height in the image description and the size */ |
(*desc)->height = newHeight; |
(*desc)->width = newWidth; |
(*desc)->dataSize = size; |
bail: |
if ( progressProc ) |
CallICMProgressProc(progressProc->progressProc,codecProgressClose,0,progressProc->progressRefCon); |
return(result); |
} |
#ifndef HAS_ASM /* we could do this part in assembly for speed if we desired */ |
#define READPIXEL(n) \ |
l = *lp++; \ |
r = (l>>16); \ |
g = (l>>8); \ |
b = l; \ |
yt = (R_W*r + G_W*g + B_W*b); \ |
if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \ |
ys[n] = yt>>16; \ |
y += yt; \ |
u += r; \ |
v += b; |
#define READPIXEL_TAB(n) \ |
l = *lp++; \ |
r = (l>>16); \ |
g = (l>>8); \ |
b = l; \ |
yt = (rwTable[r] + gwTable[g] + bwTable[b]); \ |
if ( yt > ((256L<<16)-1) ) yt = ((256L<<16)-1); \ |
ys[n] = yt>>16; \ |
y += yt; \ |
u += r; \ |
v += b; |
pascal void |
CompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg) |
{ |
register long l,*lp = (long *)baseAddr; |
register unsigned char r,g,b; |
unsigned char ys[4]; |
register long y,yt; |
short u,v; |
short rowLongs = (rowBytes>>2); |
len++; |
len>>=1; |
if ( sg->rgbwTable && *sg->rgbwTable ) { |
long *rwTable,*gwTable,*bwTable; |
rwTable = (long *)*sg->rgbwTable; |
gwTable = rwTable + 256; |
bwTable = rwTable + 512; |
while ( len-- > 0) { |
y = u = v = 0; |
READPIXEL_TAB(0); |
READPIXEL_TAB(1); |
lp += rowLongs-2; |
READPIXEL_TAB(2); |
READPIXEL_TAB(3); |
lp -= rowLongs; |
y >>= 16; |
u = (u - y)>>4; |
v = (v - y)>>4; |
l = (long)(0xfc & (ys[0])) << 24; |
l |= (long)(0xfc & (ys[1])) << 18; |
l |= (long)(0xfc & (ys[2])) << 12; |
l |= (long)(0xfc & (ys[3])) << 6; |
l |= u & 0xff; |
*(long *)data = l; |
data += sizeof(long); |
*data++ = v; |
} |
} else { |
while ( len-- > 0) { |
y = u = v = 0; |
READPIXEL(0); |
READPIXEL(1); |
lp += rowLongs-2; |
READPIXEL(2); |
READPIXEL(3); |
lp -= rowLongs; |
y >>= 16; |
u = (u - y)>>4; |
v = (v - y)>>4; |
l = (long)(0xfc & (ys[0])) << 24; |
l |= (long)(0xfc & (ys[1])) << 18; |
l |= (long)(0xfc & (ys[2])) << 12; |
l |= (long)(0xfc & (ys[3])) << 6; |
l |= u & 0xff; |
*(long *)data = l; |
data += sizeof(long); |
*data++ = v; |
} |
} |
} |
#define WRITEPIXEL \ |
r = PIN(u+y); \ |
b = PIN(v+y); \ |
y <<= 16; \ |
y -= r * R_W; \ |
y -= b * B_W; \ |
g = PIN(y / G_W); \ |
*lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b; |
#define WRITEPIXEL_TAB \ |
r = PIN(u+y); \ |
b = PIN(v+y); \ |
y <<= 16; \ |
y -= rwTable[r]; \ |
y -= bwTable[b]; \ |
g = giwTable[PIN(y>>16)]; \ |
*lp++ = (long) ( (long) r <<16) | ( (long) g <<8) | b; |
pascal void |
DecompressStrip(char *data,char *baseAddr,short rowBytes,short len,SharedGlobals *sg) |
{ |
register long y; |
register unsigned char r,g,b; |
register long l,*lp; |
long u,v; |
unsigned char ys[4]; |
short rowLongs = (rowBytes>>2); |
short blen = len; |
lp = (long *)baseAddr; |
blen++; |
blen >>= 1; |
if ( sg->rgbwTable && *sg->rgbwTable && sg->giwTable && *sg->giwTable ) { |
unsigned char *giwTable; |
long *rwTable,*bwTable; |
giwTable = (unsigned char*)*sg->giwTable; |
rwTable = (long *)*sg->rgbwTable; |
bwTable = rwTable + 512; |
while ( blen-- > 0 ) { |
l = *(long *)data; |
data += sizeof(long); |
ys[0] = (0xfc & (l>>24)); |
ys[1] = (0xfc & (l>>18)); |
ys[2] = (0xfc & (l>>12)); |
ys[3] = (0xfc & (l>>6)); |
u = (char)l; |
v = *data++; |
u<<=2; |
v<<=2; |
y = ys[0]; |
WRITEPIXEL_TAB; |
y = ys[1]; |
WRITEPIXEL_TAB; |
lp += rowLongs - 2; |
y = ys[2]; |
WRITEPIXEL_TAB; |
y = ys[3]; |
WRITEPIXEL_TAB; |
lp -= rowLongs; |
} |
} else { |
while ( blen-- > 0 ) { |
l = *(long *)data; |
data += sizeof(long); |
ys[0] = (0xfc & (l>>24)); |
ys[1] = (0xfc & (l>>18)); |
ys[2] = (0xfc & (l>>12)); |
ys[3] = (0xfc & (l>>6)); |
u = (char)l; |
v = *data++; |
u<<=2; |
v<<=2; |
y = ys[0]; |
WRITEPIXEL; |
y = ys[1]; |
WRITEPIXEL; |
lp += rowLongs - 2; |
y = ys[2]; |
WRITEPIXEL; |
y = ys[3]; |
WRITEPIXEL; |
lp -= rowLongs; |
} |
} |
} |
#endif |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14