JPEGView/Source/C/JPEG.c
Aaron Giles 92bdb55672 JPEGView 3.3 for Macintosh
These are the sources for the final official release of JPEGView for the
Mac, back in 1994.
2015-02-05 00:18:10 -08:00

1 line
34 KiB
C

/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information. */
/* Contact email: mac@aarongiles.com */
/*********************************************************/
//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================
#if THINK_C
#include "THINK.Header"
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif
//=====================================================================================
// Includes specific to this module
//=====================================================================================
#include "JPEG.h"
//=====================================================================================
// Global variables local to this module
//=====================================================================================
static Handle gJPEGDestHandle;
static CTabHandle gJFIFColors = nil;
static StdPixUPP gUnwrapPixProc = nil;
static QDBitsUPP gDummyBitsProc;
static QDTextUPP gDummyTextProc;
static QDLineUPP gDummyLineProc;
static QDRectUPP gDummyRectProc;
static QDRRectUPP gDummyRRectProc;
static QDOvalUPP gDummyOvalProc;
static QDArcUPP gDummyArcProc;
static QDPolyUPP gDummyPolyProc;
static QDRgnUPP gDummyRgnProc;
// JPEG quantization tables for identifying IJG-compressed images
static ushort gIJGLumiTab[64] = {
16, 11, 12, 14, 12, 10, 16, 14,
13, 14, 18, 17, 16, 19, 24, 40,
26, 24, 22, 22, 24, 49, 35, 37,
29, 40, 58, 51, 61, 60, 57, 51,
56, 55, 64, 72, 92, 78, 64, 68,
87, 69, 55, 56, 80, 109, 81, 87,
95, 98, 103, 104, 103, 62, 77, 113,
121, 112, 100, 120, 92, 101, 103, 99
};
static ushort gIJGChromTab[64] = {
17, 18, 18, 24, 21, 24, 47, 26,
26, 47, 99, 66, 56, 66, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99
};
//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================
static OSErr DrawJPEGQuickTime(Handle theHandle, JVDrawParamsHandle theParams);
static void InitJPEGUPPs(void);
static uchar *GetJPEGData(uchar *theAdr, long theLen, uchar theCode);
static void GetJPEGInfo(ImageHandle theImage);
static Boolean CalcIJGTable(ushort theID, uchar *theData, long *theQuality);
static Boolean CalcAppleTable(ushort theID, uchar *theData, long *theQuality);
static OSErr CompressJFIFPreview(PixMapHandle thePixMap, Rect *theRect, Handle *theHandle);
static OSErr WriteJFIFHeader(short theFile, GWorldPtr thePreview, Rect *theRect);
static OSErr MakeJPEGImageDesc(Handle theHandle, ImageDescriptionHandle theDesc);
static pascal void UnwrapPixProc(PixMap *src, Rect *srcRect, MatrixRecord *matrix,
short mode, RgnHandle mask, PixMap *matte, Rect *matteRect,
short callOldBits);
static pascal void DummyBitsProc(PixMap *src, Rect *srcRect, Rect *dstRect, short mode,
RgnHandle mask);
static pascal void DummyTextProc(short count, const void *textAddr, Point numer,
Point denom);
static pascal void DummyLineProc(Point newPt);
static pascal void DummyRectProc(GrafVerb verb, const Rect *r);
static pascal void DummyRRectProc(GrafVerb verb, const Rect *r, short ovalWidth,
short ovalHeight);
static pascal void DummyOvalProc(GrafVerb verb, const Rect *r);
static pascal void DummyArcProc(GrafVerb verb, const Rect *r, short startAngle,
short arcAngle);
static pascal void DummyPolyProc(GrafVerb verb, PolyHandle poly);
static pascal void DummyRgnProc(GrafVerb verb, RgnHandle rgn);
//=====================================================================================
// Boolean VerifyJPEGData(uchar *theData, long theSize)
//=====================================================================================
// Does a quick sanity check on the JPEG data.
//=====================================================================================
extern Boolean VerifyJPEGData(uchar *theData, long theSize)
{
return (GetJPEGData(theData, theSize, 0xd9) != nil);
}
//=====================================================================================
// Boolean idJPEG(uchar *theData, long theSize)
//=====================================================================================
// Examines the given data and attempts to identify it as a JPEG image.
//=====================================================================================
extern Boolean idJPEG(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(refNum, theSpec)
#endif
return (theSize > 4 && theData[0] == 0xff && theData[1] == 0xd8 &&
theData[2] == 0xff);
}
//=====================================================================================
// OSErr OpenJPEG(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a JPEG image.
//=====================================================================================
extern OSErr OpenJPEG(ImageHandle theImage)
{
long theLen = GetHandleSize((*theImage)->data);
FSSpec theSpec = (*theImage)->file;
Handle theComments = nil;
uchar *theData, theCode;
OSErr theErr = noErr;
short refNum;
// if (!gQTVersion) gIntError = errNoQuickTime, theErr = codecBadDataErr;
if (refNum = FSpOpenResFile(&theSpec, fsRdPerm)) {
CTabHandle theColors;
if (theColors = (CTabHandle)Get1Resource('clut', 0))
(*theImage)->ipalette = NewPalette((*theColors)->ctSize + 1, theColors,
pmTolerant, 0);
CloseResFile(refNum);
}
if (!VerifyJPEGData((uchar *)*(*theImage)->data, theLen))
gIntError = errCorruptImage, theErr = errBadJPEG;
for (theCode = 0xc0; theCode < 0xd0; theCode++)
if (theData = GetJPEGData((uchar *)*(*theImage)->data, theLen, theCode)) break;
if (theCode == 0xd0) return gIntError = errCorruptImage, codecBadDataErr;
GetJPEGInfo(theImage);
(*theImage)->compression = kJPEGCompression;
MySetRect(&(*theImage)->grect, 0, 0,
(theData[3] << 8) + theData[4], (theData[1] << 8) + theData[2]);
if (theData[5] == 3) (*theImage)->depth = 32;
else if (theData[5] == 1) {
(*theImage)->depth = 8;
(*theImage)->ipalette = NewPalette(256, nil, pmTolerant, 0);
if ((*theImage)->ipalette)
CopyPalette(GreyPalette(8), (*theImage)->ipalette, 0, 0, 256);
} else gIntError = errCorruptImage, errBadJPEG;
(*theImage)->crect = (*theImage)->qrect = (*theImage)->grect;
HLock((*theImage)->data);
ExtractComments(*(*theImage)->data, GetHandleSize((*theImage)->data), &theComments);
HUnlock((*theImage)->data);
(*theImage)->comments = theComments;
if ((*theImage)->desc.spatialQuality) {
Str63 theDesc;
BlockMove(gString[strIJGJPEG], theDesc, *gString[strIJGJPEG] + 1);
StuffNumber(theDesc, 1, (*theImage)->desc.spatialQuality);
BlockMove(theDesc, (*theImage)->compressionDesc, *theDesc + 1);
} else BlockMove((*theImage)->desc.name, (*theImage)->compressionDesc, *(*theImage)->desc.name + 1);
return theErr;
}
//=====================================================================================
// OSErr DrawJPEG(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a JPEG image, whose data is in theHandle, to the destination described in the
// drawing parameters.
//=====================================================================================
extern OSErr DrawJPEG(Handle theHandle, JVDrawParamsHandle theParams)
{
if (!gQTVersion || !gThePrefs.useQuickTime)
return DrawJPEGNoQuickTime(theHandle, theParams);
else return DrawJPEGQuickTime(theHandle, theParams);
}
//=====================================================================================
// OSErr DrawJPEG(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a JPEG using QuickTime.
//=====================================================================================
static OSErr DrawJPEGQuickTime(Handle theHandle, JVDrawParamsHandle theParams)
{
PixMapHandle dPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
NestedProgress theProgress = (*theParams)->progress;
long theSize = GetHandleSize(theHandle);
Rect srcRect = (*theParams)->bounds;
char hState = HGetState(theHandle);
ImageDescriptionHandle theDesc;
MatrixRecord theMatrix;
OSErr theErr;
KeepSpinning();
if (theDesc = (ImageDescriptionHandle)AnyNewHandle(sizeof(ImageDescription))) {
HLock((Handle)theDesc);
theErr = MakeJPEGImageDesc(theHandle, theDesc);
if (theErr == noErr) {
SetIdentityMatrix(&theMatrix);
HLock(theHandle);
FixBits((CGrafPtr)qd.thePort, true);
KeepSpinning();
if (theProgress.prog.progressProc)
CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00000000L,
theProgress.prog.progressRefCon);
InstallQDxDispatchPatch(true);
theErr = FDecompressImage(StripAddress(*theHandle), theDesc, dPixMap,
&srcRect, &theMatrix, srcCopy, qd.thePort->visRgn, nil, nil,
codecHighQuality, anyCodec, theSize, nil,
(ICMProgressProcRecordPtr)&theProgress);
RemoveQDxDispatchPatch();
if (theProgress.prog.progressProc)
CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00010000L,
theProgress.prog.progressRefCon);
if ((*theParams)->progress.aborted) theErr = codecAbortErr;
KeepSpinning();
FixBits((CGrafPtr)qd.thePort, false);
HSetState(theHandle, hState);
}
DisposeHandle((Handle)theDesc);
} else gIntError = errNoDrawMemory, theErr = memFullErr;
return theErr;
}
//=====================================================================================
// OSErr FixJPEG(ImageHandle theImage, Handle *finalData, Boolean palette)
//=====================================================================================
// "Fixes" an image so that it can be saved as a JFIF. Basically, we convert any PICTs
// to JFIF format, and save the quantized palette out to a global handle for later use
// by the saving routine.
//=====================================================================================
extern OSErr FixJPEG(ImageHandle theImage, Handle *finalData, Boolean palette)
{
OSErr theErr = noErr;
if ((*theImage)->format->inType != kPICTType &&
(*theImage)->format->inType != kSCRNType &&
(*theImage)->format->inType != kJPEGType) return errAETypeError;
if ((*theImage)->format->inType == kPICTType ||
(*theImage)->format->inType == kSCRNType) {
KeepSpinning();
if (!(*finalData = UnwrapJPEG((*theImage)->data))) return memFullErr;
} else *finalData = (*theImage)->data;
if (palette) {
if (gJFIFColors = (CTabHandle)NewHandle(4))
Palette2CTab((*theImage)->qpalette, gJFIFColors);
else return memFullErr;
} else gJFIFColors = nil;
return theErr;
}
//=====================================================================================
// OSErr SaveJPEG(FSSpec *theSpec, Handle theHandle, GWorldPtr thePreview,
// Rect *theRect, Handle privates)
//=====================================================================================
// Saves a JFIF image out to disk, including the preview image and palette resource,
// if specified.
//=====================================================================================
extern OSErr SaveJPEG(FSSpec *theSpec, Handle theHandle, GWorldPtr thePreview,
Rect *theRect, Handle privates)
{
#if applec
#pragma unused(privates)
#endif
long theSize = GetHandleSize(theHandle);
uchar *headerStart, *headerEnd, *theAdr;
char hState = HGetState(theHandle);
short theFile;
OSErr theErr;
long theLen;
KeepSpinning();
theAdr = (uchar *)(*theHandle);
if ((headerStart = GetJPEGData(theAdr, theSize, 0xe0)) && headerStart[0] == 'J' &&
headerStart[1] == 'F' && headerStart[2] == 'I' && headerStart[3] == 'F' &&
headerStart[4] == 0) {
headerStart -= 4;
headerEnd = headerStart + (headerStart[2] << 8) + headerStart[3] + 2;
while (headerEnd[1] == 0xe0 && headerEnd[4] == 'J' && headerEnd[5] == 'F' &&
headerEnd[6] == 'X' && headerEnd[7] == 'X' && headerEnd[8] == 0)
headerEnd += (headerEnd[2] << 8) + headerEnd[3] + 2;
} else if (headerStart = GetJPEGData(theAdr, theSize, 0xd8)) {
headerStart -= 2;
headerEnd = headerStart;
} else return gIntError = errCorruptImage, codecBadDataErr;
HLock(theHandle);
KeepSpinning();
theErr = FSpCreate(theSpec, kCreator, kJPEGType, 0);
if (theErr == noErr) {
KeepSpinning();
theErr = FSpOpenDF(theSpec, fsWrPerm, &theFile);
if (theErr == noErr) {
KeepSpinning();
theLen = (long)(headerStart - theAdr);
theErr = FSWrite(theFile, &theLen, theAdr);
if (theErr == noErr) {
KeepSpinning();
theSize -= theLen;
theErr = WriteJFIFHeader(theFile, thePreview, theRect);
if (theErr == noErr) {
SpinIndef();
theSize -= (long)(headerEnd - headerStart);
theErr = FSWrite(theFile, &theSize, headerEnd);
KeepSpinning();
if (theErr == noErr) {
FSClose(theFile);
HSetState(theHandle, hState);
if (gJFIFColors) {
FSpCreateResFile(theSpec, kCreator, kJPEGType, 0);
if (theFile = FSpOpenResFile(theSpec, fsRdWrPerm)) {
AddResource((Handle)gJFIFColors, 'clut', 0, gNullString);
if ((theErr = ResError()) == noErr) {
WriteResource((Handle)gJFIFColors);
ReleaseResource((Handle)gJFIFColors);
} else theErr = ResError(), gIntError = errCantWriteFile;
CloseResFile(theFile);
} else theErr = ResError(), gIntError = errCantWriteFile;
if (theErr != noErr) DisposeHandle((Handle)gJFIFColors);
}
return theErr;
} else gIntError = errCantWriteFile;
}
} else gIntError = errCantWriteFile;
FSClose(theFile);
} else gIntError = errCantOpenFile;
FSpDelete(theSpec);
} else gIntError = errCantCreateFile;
HSetState(theHandle, hState);
if (theErr != noErr && gJFIFColors) DisposeHandle((Handle)gJFIFColors);
return theErr;
}
//=====================================================================================
// void InitJPEGUPPs(void)
//=====================================================================================
// Initializes all the local UPPs for the JPEG callbacks.
//=====================================================================================
static void InitJPEGUPPs(void)
{
gUnwrapPixProc = NewStdPixProc((ProcPtr)UnwrapPixProc);
gDummyBitsProc = NewQDBitsProc((ProcPtr)DummyBitsProc);
gDummyTextProc = NewQDTextProc((ProcPtr)DummyTextProc);
gDummyLineProc = NewQDLineProc((ProcPtr)DummyLineProc);
gDummyRectProc = NewQDRectProc((ProcPtr)DummyRectProc);
gDummyRRectProc = NewQDRRectProc((ProcPtr)DummyRRectProc);
gDummyOvalProc = NewQDOvalProc((ProcPtr)DummyOvalProc);
gDummyArcProc = NewQDArcProc((ProcPtr)DummyArcProc);
gDummyPolyProc = NewQDPolyProc((ProcPtr)DummyPolyProc);
gDummyRgnProc = NewQDRgnProc((ProcPtr)DummyRgnProc);
}
//=====================================================================================
// uchar *GetJPEGData(uchar *theAdr, long theLen, uchar theCode)
//=====================================================================================
// Returns the address of the specified marker within the JPEG data stream.
//=====================================================================================
static uchar *GetJPEGData(uchar *theAdr, long theLen, uchar theCode)
{
uchar *theEnd = theAdr + theLen;
long theSize;
while (true) {
while (*theAdr++ != 0xff && theAdr < theEnd);
if (theAdr >= theEnd) return nil;
while (*theAdr == 0xff) theAdr++;
if (theAdr >= theEnd) return nil;
if (*theAdr == theCode) break;
else if (*theAdr == 0xd9) return nil;
else if (*theAdr <= 0x01 || (*theAdr >= 0xd0 && *theAdr <= 0xd8)) {
theAdr++;
continue;
}
theAdr++;
if ((theSize = (*theAdr << 8) + *(theAdr + 1)) < 0) return nil;
if ((theAdr + theSize) >= theEnd) {
*(theAdr - 1) = 0xd9;
return nil;
} else theAdr += theSize;
}
theAdr += 3;
return theAdr;
}
//=====================================================================================
// void GetJPEGInfo(ImageHandle theImage)
//=====================================================================================
// Examines the quantization tables, counts the number of components, and estimates a
// quality factor for IJG-compatible JPEGs.
//=====================================================================================
static void GetJPEGInfo(ImageHandle theImage)
{
Boolean isApple = false, isAdobe = false, isIJG = true;
Handle theHandle = (*theImage)->data;
long theLen = GetHandleSize(theHandle), theQuality = 0, len;
short components = 0;
uchar *theData;
ushort theID;
if (theData = GetJPEGData((uchar *)*theHandle, theLen, 0xfe)) {
isApple = (theData[0] == 'A' && theData[1] == 'p' && theData[2] == 'p' &&
theData[3] == 'l' && theData[4] == 'e' && theData[5] == 'M');
} else if (theData = GetJPEGData((uchar *)*theHandle, theLen, 0xee)) {
if (theData[0] == 'A' && theData[1] == 'd' && theData[2] == 'o' &&
theData[3] == 'b' && theData[4] == 'e' && theData[5] == 0)
isAdobe = true;
}
(*theImage)->desc.spatialQuality = 0;
BlockMove(gString[strUnknownJPEG], (*theImage)->desc.name, *gString[strUnknownJPEG] + 1);
if (!(theData = GetJPEGData((uchar *)*theHandle, theLen, 0xdb))) return;
do {
len = ((long)theData[-2] << 8) + theData[-1] - 2;
while (len > 0) {
theID = *theData++;
len--;
if (isApple) CalcAppleTable(theID, theData, &theQuality);
else if (isAdobe || !CalcIJGTable(theID, theData, &theQuality)) isIJG = false;
if (theID >> 4) {
theData += 128;
len -= 128;
} else {
theData += 64;
len -= 64;
}
components++;
}
theLen = StripAddress(*theHandle) + GetHandleSize(theHandle) - StripAddress(theData);
} while (theData = GetJPEGData(theData, theLen, 0xdb));
if (isApple) {
BlockMove(gString[strQuickTimeJPEG], (*theImage)->desc.name, *gString[strQuickTimeJPEG] + 1);
} else if (isAdobe) {
BlockMove(gString[strAdobeJPEG], (*theImage)->desc.name, *gString[strAdobeJPEG] + 1);
} else if (isIJG) {
(*theImage)->desc.spatialQuality = ((theQuality + 0x7fff) / components) >> 16;
BlockMove(gString[strIJGJPEG], (*theImage)->desc.name, *gString[strIJGJPEG] + 1);
}
}
//=====================================================================================
// Boolean CalcIJGTable(ushort theID, uchar *theData, long *theQuality)
//=====================================================================================
// Calculates the image quality for an IJG image.
//=====================================================================================
static Boolean CalcIJGTable(ushort theID, uchar *theData, long *theQuality)
{
long scaleFactor = 0, stdDev = 0;
ushort *theTable, entry;
register long val;
if ((theID & 0xf) == 0) theTable = gIJGLumiTab;
else if ((theID & 0xf) == 1) theTable = gIJGChromTab;
else return false;
for (entry = 0; entry < 64; entry++) {
if (theID >> 4) {
val = (theData[0] << 8) + theData[1];
theData += 2;
} else val = *theData++;
val *= 0x6400L / (long)*theTable++;
scaleFactor += val;
stdDev += (val >> 4) * (val >> 4);
}
scaleFactor >>= 6;
stdDev >>= 6;
stdDev -= (scaleFactor >> 4) * (scaleFactor >> 4);
if (stdDev > 0x400) return false;
if (scaleFactor <= 0x6400) *theQuality += ((0xc800 - scaleFactor) >> 1) << 8;
else *theQuality += ((5000 << 16) / scaleFactor) << 8;
return true;
}
//=====================================================================================
// Boolean CalcAppleTable(ushort theID, uchar *theData, long *theQuality)
//=====================================================================================
// Calculates the image quality for an Apple JPEG image (not really, but in theory).
//=====================================================================================
static Boolean CalcAppleTable(ushort theID, uchar *theData, long *theQuality)
{
#if applec
#pragma unused(theID, theData, theQuality)
#endif
return true;
}
//=====================================================================================
// void ExtractComments(Ptr theJPEG, long origLen, Handle *theComments)
//=====================================================================================
// Finds any comments records in the JPEG data stream.
//=====================================================================================
extern void ExtractComments(Ptr theJPEG, long origLen, Handle *theComments)
{
long len, i, weird, theLen = origLen;
uchar *theData;
Boolean bad;
theData = (uchar *)theJPEG;
while (true) {
if (!(theData = GetJPEGData(theData, theLen, 0xfe))) break;
len = ((long)theData[-2] << 8) + theData[-1] - 2;
bad = false;
if (theData[0] == 'A' && theData[1] == 'p' && theData[2] == 'p' &&
theData[3] == 'l' && theData[4] == 'e' && theData[5] == 'M')
bad = true;
for (weird = i = 0; i < len && !bad; i++) {
if (theData[i] >= 0x20 && theData[i] <= 0x7f) continue;
else if (theData[i] == 0x0a) theData[i] = 0x0d;
else if (theData[i] == 0x0d && theData[i + 1] == 0x0a) theData[++i] = 0x00;
else weird++;
}
if (!bad) bad = (weird > (len >> 4));
if (bad) break;
if (!*theComments) PtrToHand(theData, theComments, len);
else PtrAndHand(theData, *theComments, len);
theData += len;
theLen = StripAddress(theJPEG) + origLen - StripAddress(theData);
}
}
//=====================================================================================
// OSErr CompressJFIFPreview(PixMapHandle thePixMap, Rect *theRect,
// Handle *theHandle)
//=====================================================================================
// Compresses the JFIF preview using high quality JPEG.
//=====================================================================================
static OSErr CompressJFIFPreview(PixMapHandle thePixMap, Rect *theRect,
Handle *theHandle)
{
char pixState = GetPixelsState(thePixMap);
ImageDescriptionHandle theDesc;
long theSize, removeSize = 0;
OSErr theErr = noErr;
uchar *theData;
if (theDesc = (ImageDescriptionHandle)CheckedNewHandle(sizeof(ImageDescription), true)) {
theErr = GetMaxCompressionSize(thePixMap, theRect, 32, codecHighQuality,
kJPEGCompression, anyCodec, &theSize);
if (theErr == noErr) *theHandle = CheckedNewHandle(theSize, true);
if (*theHandle) {
LockPixels(thePixMap);
HLock(*theHandle);
theErr = CompressImage(thePixMap, theRect, codecHighQuality, kJPEGCompression,
theDesc, StripAddress(**theHandle));
HUnlock(*theHandle);
SetPixelsState(thePixMap, pixState);
if (theErr == noErr) {
theSize = (*theDesc)->dataSize;
theData = GetJPEGData((uchar *)**theHandle, theSize, 0xe0);
if (theData && theData[0] == 'J' && theData[1] == 'F' &&
theData[2] == 'I' && theData[3] == 'F' && theData[4] == 0) {
theData -= 4;
removeSize = (theData[2] << 8) + theData[3] + 2;
BlockMove(theData + removeSize, theData, theSize - removeSize);
}
SetHandleSize(*theHandle, theSize - removeSize);
} else DisposeHandle(*theHandle), *theHandle = nil;
}
DisposeHandle((Handle)theDesc);
} else theErr = memFullErr;
return theErr;
}
//=====================================================================================
// OSErr WriteJFIFHeader(short theFile, GWorldPtr thePreview, Rect *theRect)
//=====================================================================================
// Creates a new header for the active JPEG image and writes it to theFile.
//=====================================================================================
static OSErr WriteJFIFHeader(short theFile, GWorldPtr thePreview, Rect *theRect)
{
Handle theHandle, compressedPrev = nil;
short width, height, h, w, rowSize;
long theLen = 18, extLen = 0;
uchar *bp, *pp, *rowStart;
char mmuMode = true32b;
OSErr theErr;
if (thePreview) {
if (gThePrefs.compressPrev) {
PixMapHandle dstPixMap = GetGWorldPixMap(thePreview);
LockPixels(dstPixMap);
theErr = CompressJFIFPreview(dstPixMap, theRect, &compressedPrev);
UnlockPixels(dstPixMap);
}
if (!compressedPrev) {
width = Width(theRect);
height = Height(theRect);
theLen += 3 * width * height;
} else extLen += GetHandleSize(compressedPrev) + 10;
}
if (!(theHandle = CheckedNewHandle(theLen + extLen, true)))
return gIntError = errNoSaveMemory, memFullErr;
HLock(theHandle);
bp = (uchar *)StripAddress(*theHandle);
*bp++ = 0xff; *bp++ = 0xe0; // JFIF Header
*bp++ = (theLen - 2) >> 8; // Length of block
*bp++ = (theLen - 2) & 0xff;
*bp++ = 'J'; *bp++ = 'F'; // JFIF signature
*bp++ = 'I'; *bp++ = 'F';
*bp++ = 0x00;
*bp++ = 0x01; *bp++ = (compressedPrev) ? 0x02 : 0x01; // Version number = 1.01/02
*bp++ = 0x00; // Units = none
*bp++ = 0x00; *bp++ = 0x01; // Horizontal resolution
*bp++ = 0x00; *bp++ = 0x01; // Vertical resolution
if (thePreview && !compressedPrev) {
PixMapHandle thePixMap = GetGWorldPixMap(thePreview);
*bp++ = width; // Width of preview image
*bp++ = height; // Height of preview image
LockPixels(thePixMap);
rowStart = (uchar *)GetPixBaseAddr(thePixMap);
rowSize = (*thePixMap)->rowBytes & 0x3fff;
SwapMMUMode(&mmuMode);
for (h = 0; h < height; h++) {
pp = rowStart;
for (w = 0; w < width; w++) {
pp++;
*bp++ = *pp++;
*bp++ = *pp++;
*bp++ = *pp++;
}
rowStart += rowSize;
}
SwapMMUMode(&mmuMode);
UnlockPixels(thePixMap);
} else {
*bp++ = 0x00; *bp++ = 0x00; // Width and height of preview image = 0
}
if (compressedPrev) {
*bp++ = 0xff; *bp++ = 0xe0; // APP0 marker
*bp++ = (extLen - 2) >> 8; // Length of block
*bp++ = (extLen - 2) & 0xff;
*bp++ = 'J'; *bp++ = 'F'; // JFXX signature
*bp++ = 'X'; *bp++ = 'X';
*bp++ = 0x00;
*bp++ = 0x10; // JPEG-coded preview extension
BlockMove(*compressedPrev, bp, GetHandleSize(compressedPrev));
DisposeHandle(compressedPrev);
}
KeepSpinning();
theLen += extLen;
theErr = FSWrite(theFile, &theLen, *theHandle);
KeepSpinning();
if (theErr != noErr) gIntError = errCantWriteFile;
DisposeHandle(theHandle);
return theErr;
}
//=====================================================================================
// OSErr MakeJPEGImageDesc(Handle theHandle, ImageDescriptionHandle theDesc)
//=====================================================================================
// Creates a QuickTime image description record based on JPEG data.
//=====================================================================================
static OSErr MakeJPEGImageDesc(Handle theHandle, ImageDescriptionHandle theDesc)
{
long theLen = GetHandleSize(theHandle);
uchar *theData;
short theCode;
for (theCode = 0xc0; theCode < 0xd0; theCode++)
if (theData = GetJPEGData((uchar *)*theHandle, theLen, theCode)) break;
if (theCode == 0xd0) return gIntError = errCorruptImage, codecBadDataErr;
(*theDesc)->idSize = sizeof(ImageDescription);
(*theDesc)->cType = kJPEGCompression;
(*theDesc)->resvd1 = (*theDesc)->resvd2 = (*theDesc)->dataRefIndex = 0;
BlockMove(gJPEGInfo.typeName, (*theDesc)->name, 32);
(*theDesc)->version = gJPEGInfo.version;
(*theDesc)->revisionLevel = gJPEGInfo.revisionLevel;
(*theDesc)->vendor = gJPEGInfo.vendor;
(*theDesc)->temporalQuality = 0;
(*theDesc)->spatialQuality = 0x200L;
(*theDesc)->width = (theData[3] << 8) + theData[4];
(*theDesc)->height = (theData[1] << 8) + theData[2];
(*theDesc)->hRes = (*theDesc)->vRes = 0x480000L;
(*theDesc)->dataSize = theLen;
(*theDesc)->frameCount = 1;
(*theDesc)->depth = 32;
(*theDesc)->clutID = -1;
return noErr;
}
//=====================================================================================
// Handle UnwrapJPEG(Handle srcHandle)
//=====================================================================================
// Extracts (unwraps) the original JPEG data from a JPEG PICT image.
//=====================================================================================
extern Handle UnwrapJPEG(Handle srcHandle)
{
long theSize = GetHandleSize(srcHandle);
char hState = HGetState(srcHandle);
CQDProcs theProcs, *oldProcs;
KeepSpinning();
if (MakeMemAvailable(theSize) && (gJPEGDestHandle = NewHandle(theSize))) {
HLockHi(srcHandle);
PushPort();
MySetPort(gGenericGWorld);
SetStdCProcs(&theProcs);
if (!gUnwrapPixProc) InitJPEGUPPs();
theProcs.newProc1 = (UniversalProcPtr)gUnwrapPixProc;
theProcs.bitsProc = gDummyBitsProc;
theProcs.textProc = gDummyTextProc;
theProcs.lineProc = gDummyLineProc;
theProcs.rectProc = gDummyRectProc;
theProcs.rRectProc = gDummyRRectProc;
theProcs.ovalProc = gDummyOvalProc;
theProcs.arcProc = gDummyArcProc;
theProcs.polyProc = gDummyPolyProc;
theProcs.rgnProc = gDummyRgnProc;
oldProcs = gGenericGWorld->grafProcs;
gGenericGWorld->grafProcs = &theProcs;
KeepSpinning();
DrawTrimmedPicture((PicHandle)srcHandle, &(*(PicHandle)srcHandle)->picFrame, nil,
false, nil);
gGenericGWorld->grafProcs = oldProcs;
PopPort();
HSetState(srcHandle, hState);
return gJPEGDestHandle;
} else gIntError = errNoUnwrapMemory;
return nil;
}
//=====================================================================================
// Handle UnwrapJPEG(Handle srcHandle)
//=====================================================================================
// Wraps a JPEG image into a PICT.
//=====================================================================================
extern Handle WrapJPEG(Handle srcHandle, short depth)
{
PixMapHandle thePixMap = GetGWorldPixMap(gGenericGWorld);
long theSize = GetHandleSize(srcHandle);
char hState = HGetState(srcHandle);
ImageDescriptionHandle theDesc;
OpenCPicParams theParams;
Handle theHandle;
OSErr theErr;
KeepSpinning();
HLock(srcHandle);
if (MakeMemAvailable(theSize) &&
(theDesc = (ImageDescriptionHandle)CheckedNewHandle(sizeof(ImageDescription), true))) {
theErr = MakeJPEGImageDesc(srcHandle, theDesc);
if (depth == 8 || depth == 40) (*theDesc)->depth = 40;
if (theErr == noErr) {
MySetRect(&theParams.srcRect, 0, 0, (*theDesc)->width, (*theDesc)->height);
theParams.hRes = (*theDesc)->hRes;
theParams.vRes = (*theDesc)->vRes;
theParams.version = -2;
theParams.reserved1 = theParams.reserved2 = 0;
LockPixels(thePixMap);
PushPort();
MySetPort(gGenericGWorld);
ClipRect(&theParams.srcRect);
if (theHandle = (Handle)OpenCPicture(&theParams)) {
KeepSpinning();
theErr = FDecompressImage(StripAddress(*srcHandle), theDesc,
thePixMap, &theParams.srcRect, nil, ditherCopy, nil, nil, nil,
codecHighQuality, anyCodec, theSize, nil, nil);
KeepSpinning();
UnlockPixels(thePixMap);
ClosePicture();
if (theErr == noErr && !EmptyRect(&(*(PicHandle)theHandle)->picFrame)) {
PopPort();
DisposeHandle((Handle)theDesc);
HSetState(srcHandle, hState);
return theHandle;
}
DisposeHandle(theHandle);
} else gIntError = errNoWrapMemory;
PopPort();
}
DisposeHandle((Handle)theDesc);
} else gIntError = errNoWrapMemory;
HSetState(srcHandle, hState);
return nil;
}
//=====================================================================================
// pascal void UnwrapPixProc(PixMap *src, Rect *srcRect, MatrixRecord *matrix,
// short mode, RgnHandle mask, PixMap *matte, Rect *matteRect,
// short callOldBits)
//=====================================================================================
// Bottleneck function called to extract raw JPEG data from an image.
//=====================================================================================
static pascal void UnwrapPixProc(PixMap *src, Rect *srcRect, MatrixRecord *matrix,
short mode, RgnHandle mask, PixMap *matte, Rect *matteRect,
short callOldBits)
{
#if applec
#pragma unused(srcRect, matrix, mode, mask, matte, matteRect, callOldBits)
#endif
long bufSize, left, offset = 0;
ImageDescriptionHandle theDesc;
ICMProgressProcRecord progProc;
ICMDataProcRecord dataProc;
Ptr theData, destAdr;
KeepSpinning();
if (!GetCompressedPixMapInfo(src, &theDesc, &theData, &bufSize, &dataProc, &progProc)) {
if ((*theDesc)->cType == kJPEGCompression) {
HLock(gJPEGDestHandle);
destAdr = (Ptr)*gJPEGDestHandle;
left = (*theDesc)->dataSize;
if (dataProc.dataProc) {
while (left > bufSize) {
BlockMove(theData, &destAdr[offset], bufSize);
offset += bufSize;
left -= bufSize;
theData += bufSize;
if (CallICMDataProc(dataProc.dataProc, &theData,
(left > bufSize) ? bufSize : left,
dataProc.dataRefCon)) return;
}
}
BlockMove(theData, &destAdr[offset], left);
HUnlock(gJPEGDestHandle);
}
}
}
//=====================================================================================
// Dummy QuickDraw bottlenecks to filter out non-PixMaps within PICTs
//=====================================================================================
static pascal void DummyBitsProc(PixMap *src, Rect *srcRect, Rect *dstRect, short mode,
RgnHandle mask)
{
#if applec
#pragma unused(src, srcRect, dstRect, mode, mask)
#endif
}
static pascal void DummyTextProc(short count, const void *textAddr, Point numer,
Point denom)
{
#if applec
#pragma unused(count, textAddr, numer, denom)
#endif
}
static pascal void DummyLineProc(Point newPt)
{
#if applec
#pragma unused(newPt)
#endif
}
static pascal void DummyRectProc(GrafVerb verb, const Rect *r)
{
#if applec
#pragma unused(verb, r)
#endif
}
static pascal void DummyRRectProc(GrafVerb verb, const Rect *r, short ovalWidth,
short ovalHeight)
{
#if applec
#pragma unused(verb, r, ovalWidth, ovalHeight)
#endif
}
static pascal void DummyOvalProc(GrafVerb verb, const Rect *r)
{
#if applec
#pragma unused(verb, r)
#endif
}
static pascal void DummyArcProc(GrafVerb verb, const Rect *r, short startAngle,
short arcAngle)
{
#if applec
#pragma unused(verb, r, startAngle, arcAngle)
#endif
}
static pascal void DummyPolyProc(GrafVerb verb, PolyHandle poly)
{
#if applec
#pragma unused(verb, poly)
#endif
}
static pascal void DummyRgnProc(GrafVerb verb, RgnHandle rgn)
{
#if applec
#pragma unused(verb, rgn)
#endif
}