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.
JFIF Preview Component/JFIFPreviewer.c
/* |
File: JFIFPreviewer.c |
Contains: |
Written by: |
Copyright: Copyright © 1984-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): |
8/16/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 |
*/ |
#include <Aliases.h> |
#include <Files.h> |
#include <Errors.h> |
#include <ToolUtils.h> |
#include <Memory.h> |
#include <QDOffscreen.h> |
#include <Components.h> |
#include <ImageCompression.h> |
#include <QuicktimeComponents.h> |
#include <FixMath.h> |
enum{ |
uppCanDoSelectorProcInfo=kPascalStackBased |
| RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) |
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(short))), |
uppshowJFIFPreviewProcInfo=kPascalStackBased |
| RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) |
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(OSType))) |
| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(PicHandle))) |
| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(Rect*))), |
uppmakeJFIFPreviewProcInfo=kPascalStackBased |
| RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) |
| STACK_ROUTINE_PARAMETER(1,SIZE_CODE(sizeof(OSType*))) |
| STACK_ROUTINE_PARAMETER(2,SIZE_CODE(sizeof(Handle*))) |
| STACK_ROUTINE_PARAMETER(3,SIZE_CODE(sizeof(const FSSpec*))) |
| STACK_ROUTINE_PARAMETER(4,SIZE_CODE(sizeof(ICMProgressProcRecordPtr))) |
}; |
pascal ComponentResult PreviewShowData(pnotComponent p, OSType dataType, Handle data, |
const Rect *inHere) |
{ |
#pragma unused(p,dataType,data,inHere) |
ComponentCallNow(1, 12); |
return 0; |
} |
/* |
NOTE: to install this previewer ( or any other components ) inside your application: |
put all the resources into your app then do this: |
{ |
Handle h; |
short i,c; |
short myAppFile; // the res file refnum of your app = current res file at launch |
Boolean everyBody = false; // set to true to let other apps use component |
UseResFile(myAppFile); |
c = Count1Resources('thng'); |
for ( i=1; i <= c; i++ ) |
h = Get1IndResource('thng',i); |
RegisterComponentResource((ComponentResourceHandle)h,everyBody); |
ReleaseResource(h); |
} |
} |
*/ |
#define DEFAULT_COMPRESSION 'rpza' // compressor for created previews - zero for uncompressed |
#define DEFAULT_QUALITY codecHighQuality // quality level for created previews |
#define SBSIZE 2048 // size of scan buffer for scanning for JFIF header |
/*********** |
struct for deep progressProc. |
***********/ |
typedef struct { |
ICMProgressProcRecordPtr progress; |
GDHandle gd; |
CGrafPtr port; |
Fixed start,end; |
} adpRec; |
/*********** |
struct for spooling with dataProcs. |
***********/ |
typedef struct { |
Ptr bufEnd; |
Ptr bufStart; |
long bufSize; |
long size; |
short refNum; |
} DPRec; |
/*********** |
function prototypes |
**********/ |
pascal ComponentResult JFIFPreviewDispatch( ComponentParameters *params, Handle storage ); |
pascal ComponentResult showJFIFPreview(OSType dataType, PicHandle data, const Rect *inHere); |
pascal ComponentResult makeJFIFPreview(OSType *previewType, Handle *previewResult, |
const FSSpec *sourceFile, ICMProgressProcRecordPtr progress); |
pascal ComponentResult CanDoSelector(short selector); |
pascal OSErr adpProc(short msg,Fixed pct,long refcon); |
pascal OSErr dataProc(Ptr *cp,long sizeNeeded,long refcon); |
OSErr ReadJFIFThumbnail(short refNum, Handle *thumbnail, ICMProgressProcRecordPtr progress, Boolean thumbOnly); |
/*************************************************** |
Component entry point. |
***************************************************/ |
#ifdef powerc |
ProcInfoType __procinfo=kPascalStackBased |
| RESULT_SIZE(SIZE_CODE(sizeof(ComponentResult))) |
| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ComponentParameters*))) |
| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(Handle))); |
#endif |
pascal ComponentResult JFIFPreviewDispatch( ComponentParameters *params, Handle storage ) |
{ |
#pragma unused(storage) |
ComponentFunctionUPP componentProc = 0; |
ComponentResult err = 0; |
switch (params->what) { |
case kComponentOpenSelect: |
#ifdef THINK_C |
SetComponentInstanceA5((ComponentInstance)params->params[0], *(long *)CurrentA5); |
#endif |
break; |
case kComponentCloseSelect: |
break; |
case kComponentCanDoSelect: |
return CallComponentFunction(params,NewRoutineDescriptor((ProcPtr)CanDoSelector, uppCanDoSelectorProcInfo, GetCurrentArchitecture())); |
break; |
case kComponentVersionSelect: |
return 0; |
break; |
case 1: |
componentProc = NewRoutineDescriptor((ProcPtr)showJFIFPreview,uppshowJFIFPreviewProcInfo,GetCurrentArchitecture()); |
break; |
case 2: |
componentProc = NewRoutineDescriptor((ProcPtr)makeJFIFPreview,uppmakeJFIFPreviewProcInfo,GetCurrentArchitecture()); |
break; |
} |
if (componentProc) |
err = CallComponentFunction(params, componentProc); |
return err; |
} |
/*************************************************** |
Indicate whether we can handle the call or not. |
***************************************************/ |
pascal ComponentResult |
CanDoSelector(short selector) |
{ |
switch (selector) { |
case kComponentOpenSelect: |
case kComponentCloseSelect: |
case kComponentCanDoSelect: |
case kComponentVersionSelect: |
case 1: |
case 2: |
return(true); |
default: |
return (false); |
} |
} |
/*************************************************** |
We are asked to make a thumbnail from a JFIF file. We comply. |
***************************************************/ |
pascal ComponentResult makeJFIFPreview(OSType *previewType, Handle *previewResult, |
const FSSpec *sourceFile, ICMProgressProcRecordPtr progress) |
{ |
OSErr err; |
Handle thumbnail = 0; |
short refNum; |
err = FSpOpenDF(sourceFile, fsRdPerm, &refNum); |
*previewResult = nil; |
if (!err) { |
err = ReadJFIFThumbnail(refNum, previewResult, progress, false); |
FSClose(refNum); |
} |
bail: |
if (!err) { |
*previewType = 'PICT'; |
} else if ( *previewResult ) { |
DisposeHandle(*previewResult); |
*previewResult = nil; |
} |
return(err); |
} |
/*************************************************** |
Called to show the preview. Data is a picture, type is 'PICT'; |
***************************************************/ |
pascal ComponentResult showJFIFPreview(OSType dataType, PicHandle data, const Rect *inHere) |
{ |
OSErr err = noErr; |
Handle thumbnail = 0; |
short refNum; |
FSSpec theFile; |
Boolean whoCares; |
Handle thePict = nil; |
ComponentInstance ci; |
if (dataType != rAliasType) |
return paramErr; |
if (err = ResolveAlias(nil, (AliasHandle)data, &theFile, &whoCares)) goto bail; |
err = FSpOpenDF(&theFile, fsRdPerm, &refNum); |
if (!err) { |
err = ReadJFIFThumbnail(refNum, &thePict, nil, true); |
FSClose(refNum); |
} |
if (err) goto bail; |
if (ci = OpenDefaultComponent('pnot', 'PICT')) { |
PreviewShowData(ci, 'PICT', thePict, inHere); |
CloseComponent(ci); |
} |
KillPicture((PicHandle)thePict); |
bail: |
return err; |
} |
/*************************************************** |
This does the work. It checks for a JFIF thumbnail and if there is one, |
it makes a PICT out of it. If not it draws the JFIF file into a 96x96 (max) |
offscreen and makes a PICT out of that. |
***************************************************/ |
OSErr |
ReadJFIFThumbnail(short refNum, Handle *thumbnail, ICMProgressProcRecordPtr progress, Boolean thumbOnly) |
{ |
long refcon = 0; |
unsigned char buf[SBSIZE]; |
long l = SBSIZE; |
short i; |
short j,w = 0,h = 0; |
Rect frame; |
PicHandle pict = nil; |
GWorldPtr gw = nil; |
CGrafPtr savePort; |
GDHandle saveGD; |
unsigned char *bp,*rp; |
short rb; |
char mmuMode = 1; |
char oldMMUMode; |
short x,y; |
Ptr tp; |
short err = 0; |
short numCom = 0; |
short hv1 =0,hv2=0,hv3=0; |
/* call the progress proc if we have to. */ |
if ( progress) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressOpen,0,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x1,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
} |
GetGWorld(&savePort,&saveGD); |
FSRead(refNum,&l,(Ptr)buf); |
/* scan the JFIF stream for a JFIF header, or a SOF header */ |
for ( i=0; i < SBSIZE; i++ ) { |
if ( buf[i] == 0xff ) { |
i++; |
/* JFIF header */ |
if ( buf[i] == (unsigned char)0xe0 ) { |
i++; |
j = (buf[i] << 8) | buf[i+1]; |
i++; |
if ( j <= 16 ) /* no thumbnail - keep scanning */ |
continue; |
if ( buf[i+1] == 'J' && |
buf[i+2] == 'F' && |
buf[i+3] == 'I' && |
buf[i+4] == 'F' && |
buf[i+5] == 0 && |
buf[i+6] == 1 && |
buf[i+7] <= 1 ) { |
w = buf[i+13]; |
h = buf[i+14]; |
if ( w == 0 || h == 0 ) /* no thumbnail - keep scanning */ |
continue; |
else { |
/* read in the thumbnail - its 8-8-8 rgb for w*h pixels */ |
l = w*h*3; |
SetFPos(refNum,fsFromStart,(long)i+15); |
i = 0; |
tp = NewPtr(l); |
if ( tp == nil ) { |
err = -108; |
goto done; |
} |
if ( progress) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x0100,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
} |
FSRead(refNum,&l,tp); |
SetRect(&frame,0,0,w,h); |
/* make a 16-bit gworld and copy the thumbnail into it */ |
if ( NewGWorld(&gw,16,&frame,nil,nil,0) == 0 || |
NewGWorld(&gw,16,&frame,nil,nil,8) == 0 ) { |
LockPixels(gw->portPixMap); |
bp = (unsigned char *)GetPixBaseAddr(gw->portPixMap); |
rb = (*gw->portPixMap)->rowBytes & 0x3fff; |
oldMMUMode = GetMMUMode(); |
SwapMMUMode(&mmuMode); |
for ( y=0; y < h; y++ ) { |
rp = bp; |
if ( progress) { |
SwapMMUMode(&oldMMUMode); |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x1000 + (0x9000 * y) / h,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
SwapMMUMode(&oldMMUMode); |
} |
for ( x=0; x < w; x++ ) { |
short pix = (0x1f & (tp[i++]>>3)) << 10; |
pix |= (0x1f & (tp[i++]>>3)) << 5; |
pix |= (0x1f & (tp[i++]>>3)); |
*(short *)rp = pix; |
rp += 2; |
} |
bp += rb; |
} |
SwapMMUMode(&mmuMode); |
DisposePtr(tp); |
break; /* okay we got it - get outof the scan loop */ |
} else { |
err = -108; |
goto done; |
} |
} |
} else { |
err = -108; // no memory |
goto done; |
} |
/* start of frame header - grab the width and height */ |
} else if ( buf[i] == (unsigned char)0xc0 ) { |
i += 4; |
h = (buf[i]<<8) | buf[i+1]; |
i += 2; |
w = (buf[i]<<8) | buf[i+1]; |
i += 2; |
numCom = buf[i]; |
if ( numCom == 3 ) { |
i += 2; |
hv1 = buf[i]; |
i += 3; |
hv2 = buf[i]; |
i += 3; |
hv3 = buf[i]; |
} |
break; |
} |
} |
} |
/* couldn't find a SOF header - so bail */ |
if ( w == 0 || h == 0 ) { |
err = -50; |
goto done; |
} |
/* there was no thumbnail - so draw the whole image */ |
if ( gw == nil ) { |
ImageDescriptionHandle desc = nil; |
ICMDataProcRecord dataP,*dataPP = nil; |
DPRec dataRec; |
ICMProgressProcRecord pproc; |
adpRec adpRec; |
MatrixRecord matrix; |
short tw,th; |
Rect tframe; |
Ptr buffer = nil; |
if (thumbOnly) { |
err = -50; |
goto done; |
} |
if ( (desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription))) == nil ) { |
err = MemError(); |
goto cbail; |
} |
/* make up an image description */ |
(*desc)->idSize = sizeof(ImageDescription); |
(*desc)->temporalQuality = 0; |
(*desc)->spatialQuality = codecNormalQuality; |
(*desc)->dataSize = l; |
(*desc)->cType = 'jpeg'; |
(*desc)->version = 0; |
(*desc)->revisionLevel = 0; |
(*desc)->vendor = 0; |
(*desc)->hRes = 72L<<16; |
(*desc)->vRes = 72L<<16; |
(*desc)->width = w; |
(*desc)->height = h; |
(*desc)->depth = 32; |
(*desc)->clutID = -1; |
BlockMove("\pPhoto - JPEG",(*desc)->name,13); |
if ( w > h ) { |
tw = 96; |
th = h * 96 / w ; |
} else { |
th = 96; |
tw = w * 96 / h ; |
} |
SetRect(&tframe,0,0,tw,th); |
SetRect(&frame,0,0,w,h); |
/* make a gworld up to 96x96 pixels and draw the JFIF file in it */ |
if ( NewGWorld(&gw,16,&tframe,nil,nil,0) == 0 || |
NewGWorld(&gw,16,&tframe,nil,nil,8) == 0 ) { |
if ( progress ) { |
/* make a progressproc to call for partial progress */ |
adpRec.progress = progress; |
adpRec.port = savePort; |
adpRec.gd = saveGD; |
adpRec.start = 0x1000; |
adpRec.end = 0xa000; |
pproc.progressProc = NewICMProgressProc(adpProc); |
pproc.progressRefCon = (long)&adpRec; |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x0100,progress->progressRefCon) ) { |
err = -1; |
goto cbail; |
} |
} |
/* read in file */ |
GetEOF(refNum,&l); |
if ( (buffer = NewPtr(l)) == nil ) { |
/* if we cant fit the whole thing in memory - make a dataProc to spool it in */ |
if ( (buffer= NewPtr(codecMinimumDataSize)) == nil ) { |
err = MemError(); |
goto cbail; |
} |
dataRec.refNum = refNum; |
dataRec.bufStart = buffer; |
dataRec.bufEnd = buffer + codecMinimumDataSize; |
dataRec.size = l - codecMinimumDataSize; |
dataRec.bufSize = codecMinimumDataSize; |
dataPP = &dataP; |
dataP.dataRefCon = (long)&dataRec; |
dataP.dataProc = NewICMDataProc(dataProc); |
l = codecMinimumDataSize; |
} |
if ( progress ) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x0800,progress->progressRefCon) ) { |
err = -1; |
goto cbail; |
} |
} |
SetFPos(refNum,fsFromStart,0); |
if ( (err=FSRead(refNum,&l,buffer)) != 0 ) |
goto cbail; |
if ( progress ) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x1000,progress->progressRefCon) ) { |
err = -1; |
goto cbail; |
} |
} |
RectMatrix(&matrix,&frame,&tframe); |
SetGWorld(gw,nil); |
err=FDecompressImage(buffer,desc,gw->portPixMap, |
&frame,&matrix,ditherCopy,(RgnHandle)nil, |
(PixMapHandle)nil,(Rect *)nil,codecHighQuality,anyCodec,0, |
dataPP,progress ? &pproc : nil); |
frame = tframe; |
cbail: |
SetGWorld(savePort,saveGD); |
if ( buffer ) |
DisposePtr(buffer); |
if ( desc ) |
DisposeHandle((Handle)desc); |
if ( err ) |
goto done; |
} else { |
err = -108; |
goto done; |
} |
} |
/* if we get here than gw holds the image for the thumbnail - put it in a PICT and compress it */ |
if ( gw ) { |
if ( progress) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0xa000,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
} |
SetGWorld(gw,nil); |
pict = OpenPicture(&frame); |
ClipRect(&frame); |
EraseRect(&frame); |
CopyBits((BitMap *)*gw->portPixMap,(BitMap *)*gw->portPixMap, |
&gw->portRect,&gw->portRect,ditherCopy,nil); |
ClosePicture(); |
SetGWorld(savePort,saveGD); |
if (thumbOnly) { |
*thumbnail = (Handle)pict; |
goto done; |
} |
if ( progress) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0xb000,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
} |
DisposeGWorld(gw); |
gw = nil; |
if ( GetHandleSize((Handle)pict) <= sizeof(Picture) ) { |
DisposeHandle((Handle)pict); |
err = -108; // no memory |
} else { |
*thumbnail = NewHandle(sizeof(Picture)); |
if ( progress) { |
if ( CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0xc000,progress->progressRefCon) ) { |
err = -1; |
goto done; |
} |
} |
if ( DEFAULT_COMPRESSION != 0 ) { |
if ( (err=CompressPicture(pict,(PicHandle)*thumbnail,DEFAULT_QUALITY,DEFAULT_COMPRESSION)) != 0 ) { |
DisposeHandle(*thumbnail); |
*thumbnail = nil; |
DisposeHandle((Handle)pict); |
goto done; |
} |
DisposeHandle((Handle)pict); |
} else { |
*thumbnail = (Handle)pict; |
} |
if ( progress) |
CallICMProgressProc(progress->progressProc,codecProgressUpdatePercent,0x10000,progress->progressRefCon); |
} |
} |
done: |
if ( gw ) |
DisposeGWorld(gw); |
SetGWorld(savePort,saveGD); |
if ( progress) |
CallICMProgressProc(progress->progressProc,codecProgressClose,0,progress->progressRefCon); |
return(err); |
} |
/*************************************************** |
Deep progressproc - scales calls from proc and passes them up to higher level. |
***************************************************/ |
pascal OSErr |
adpProc(short msg,Fixed pct,long refcon) |
{ |
OSErr res = 0; |
CGrafPtr savePort; |
GDHandle saveGD; |
adpRec *adpr = (adpRec *)refcon; |
if ( msg == codecProgressUpdatePercent ) { |
pct = adpr->start + FixMul(pct,adpr->end-adpr->start); |
GetGWorld(&savePort,&saveGD); |
SetGWorld(adpr->port,adpr->gd); |
res = CallICMProgressProc(adpr->progress->progressProc,msg,pct,adpr->progress->progressRefCon); |
SetGWorld(savePort,saveGD); |
} |
return(res); |
} |
/*************************************************** |
DataProc for spooling in compressed data. |
***************************************************/ |
pascal OSErr |
dataProc(Ptr *cp,long sizeNeeded,long refcon) |
{ |
char mode = 0; |
Ptr current; |
long newChunk; |
long leftOver; |
long size; |
OSErr result = noErr; |
DPRec *dpr = (DPRec *)refcon; |
if ( cp == nil ) |
return(-1); /* dont do random access yet */ |
current = *cp; |
SwapMMUMode(&mode); |
if ( (current + sizeNeeded) > dpr->bufEnd ) { |
// move whats left up to the front of the buffer |
leftOver = dpr->bufEnd - current; |
BlockMove(current,dpr->bufStart,leftOver); |
newChunk = dpr->bufSize - leftOver; |
if ( dpr->size < newChunk ) |
newChunk = dpr->size; |
if ( newChunk ) { |
size = newChunk; |
FSRead(dpr->refNum,&size,dpr->bufStart+leftOver); |
dpr->size -= newChunk; |
dpr->bufEnd = dpr->bufStart+leftOver+newChunk; |
} |
*cp = dpr->bufStart; |
} |
SwapMMUMode(&mode); |
return(result); |
} |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-01-14