JPEGView/Source/C/GIF.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
29 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"
#include <stddef.h>
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif
//=====================================================================================
// Includes specific to this module
//=====================================================================================
#include "GIF.h"
//=====================================================================================
// GIFData: structure holding information needed to decompress a GIF image; passed
// between the C and assembly language portions of this module.
//=====================================================================================
typedef struct GIFData {
uchar *src; /* 0 */
uchar *dest; /* 4 */
uchar *stack; /* 8 */
uchar *end; /* 12 */
ulong *tree; /* 16 */
#if USE_ASM
ulong store[12]; /* 20 */ // this is specific to the 68020 implementation
#endif
ushort charSize; /* 68 */
short width; /* 70 */
Boolean theReturn; /* 72 */
} GIFData, *GIFDataPtr;
//=====================================================================================
// GIFPrivates: private data structure for GIF images, maintained for each image.
//=====================================================================================
typedef struct GIFPrivates
{
long totalLines;
} GIFPrivates, *GIFPrivatesPtr, **GIFPrivatesHandle;
//=====================================================================================
// Global variables local to this module
//=====================================================================================
static long gTotalLines, gCurrentLines;
static GIFData gGIFData;
static JVDrawParamsHandle gGIFParams;
// globals specific to the C implementation of GIF decoding
#if !USE_ASM
static ushort gTreeSize, gCodeSize, gBytesLeft, gClearCode, gEndCode;
static ushort gFirstCode, gLastCode;
static ulong gBitBuffer, gBitsInBuffer;
static uchar *gStack, *gInput;
#endif
//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================
static Size FindNextGIFImage(Handle theData, Size startOffset);
static OSErr ExtractGIFComments(Handle theData, Handle *theComments);
static GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData);
static PaletteHandle MakeGIFPalette(uchar *theData, short theSize);
static CTabHandle MakeGIFCTable(uchar *theData);
static OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect,
Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc);
static Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth,
short iheight, Rect *sRect, short *rowCount,
ICMProgressProcRecordPtr progProc);
static Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth,
short iheight, Rect *sRect, short *rowCount,
ICMProgressProcRecordPtr progProc);
static Boolean DecodeRow(short width, uchar *src, uchar *dest);
#if USE_ASM && !THINK_C
extern void DoDecodeRow(GIFDataPtr theData);
extern void DoDecodeDummyRow(GIFDataPtr theData);
#else
static void DoDecodeRow(GIFDataPtr theData);
static void DoDecodeDummyRow(GIFDataPtr theData);
#endif
//=====================================================================================
// Boolean idGIF(uchar *theData, long theSize)
//=====================================================================================
// Examines the given data and attempts to identify it as a GIF image.
//=====================================================================================
extern Boolean idGIF(uchar *theData, long theSize, short refNum, FSSpec *theSpec)
{
#if applec
#pragma unused(refNum, theSpec)
#endif
return (theSize > 6 &&
theData[0] == 'G' &&
theData[1] == 'I' &&
theData[2] == 'F' &&
theData[3] == '8' &&
(theData[4] == '7' || theData[4] == '9') &&
theData[5] == 'a');
}
//=====================================================================================
// OSErr OpenGIF(ImageHandle theImage)
//=====================================================================================
// Initializes the image record for a GIF image.
//=====================================================================================
extern OSErr OpenGIF(ImageHandle theImage)
{
long theLen = GetHandleSize((*theImage)->data), totalLines;
char hState = HGetState((*theImage)->data);
Rect imageRect = { 0, 0, 0, 0 };
Size imageOffset;
Handle theHandle;
uchar *theData;
if (!((*theImage)->privateData = NewHandleClear(sizeof(GIFPrivates))))
return memFullErr;
HLock((*theImage)->data);
theData = (uchar *)*(*theImage)->data;
SetRect(&(*theImage)->grect, 0, 0, (theData[7] << 8) + theData[6],
(theData[9] << 8) + theData[8]);
(*theImage)->compression = kGIFType;
BlockMove(gString[strLZWGIF], (*theImage)->desc.name, gString[strLZWGIF][0] + 1);
imageOffset = FindNextGIFImage((*theImage)->data, 0);
if (imageOffset != -1) {
theData = (uchar *)*(*theImage)->data + imageOffset;
if (theData[9] & 0x80) {
(*theImage)->depth = (theData[9] & 7) + 1;
(*theImage)->ipalette = MakeGIFPalette(theData + 10, (*theImage)->depth);
} else {
(*theImage)->depth = (*((uchar *)*(*theImage)->data + 10) & 7) + 1;
(*theImage)->ipalette = MakeGIFPalette((uchar *)*(*theImage)->data + 13,
(*theImage)->depth);
}
ExtractGIFComments((*theImage)->data, &theHandle);
(*theImage)->comments = theHandle;
imageRect.top = (theData[4] << 8) + theData[3];
imageRect.left = (theData[2] << 8) + theData[1];
imageRect.bottom = imageRect.top + (theData[8] << 8) + theData[7];
imageRect.right = imageRect.left + (theData[6] << 8) + theData[5];
totalLines = Height(&imageRect);
(*theImage)->grect = imageRect;
while ((imageOffset = FindNextGIFImage((*theImage)->data, imageOffset)) != -1) {
theData = (uchar *)*(*theImage)->data + imageOffset;
imageRect.top = (theData[4] << 8) + theData[3];
imageRect.left = (theData[2] << 8) + theData[1];
imageRect.bottom = imageRect.top + (theData[8] << 8) + theData[7];
imageRect.right = imageRect.left + (theData[6] << 8) + theData[5];
totalLines += Height(&imageRect);
UnionRect(&imageRect, &(*theImage)->grect, &(*theImage)->grect);
}
} else return gIntError = errCorruptImage, codecBadDataErr;
(*theImage)->qrect = (*theImage)->crect = (*theImage)->grect;
HSetState((*theImage)->data, hState);
(*(GIFPrivatesHandle)(*theImage)->privateData)->totalLines = totalLines;
BlockMove(gString[strLZWGIF], (*theImage)->compressionDesc, *gString[strLZWGIF] + 1);
return noErr;
}
//=====================================================================================
// OSErr DrawGIF(Handle theHandle, JVDrawParamsHandle theParams)
//=====================================================================================
// Draws a GIF image, whose data is in theHandle, to the destination described in the
// drawing parameters.
//=====================================================================================
extern OSErr DrawGIF(Handle theHandle, JVDrawParamsHandle theParams)
{
NestedProgress theProgress = (*theParams)->progress;
long dataLen = GetHandleSize(theHandle);
Rect srcRect = (*theParams)->bounds;
char hState = HGetState(theHandle);
PixMapHandle gifPixMap;
Handle gifDataHandle;
OSErr theErr = noErr;
Size imageOffset = 0;
GWorldPtr gifGWorld;
uchar *dataStart;
gTotalLines = (*(GIFPrivatesHandle)(*theParams)->privateData)->totalLines;
gCurrentLines = 0;
gGIFParams = theParams;
gifDataHandle = NewHandleClear(4096 + 4096 * 4);
if (!gifDataHandle) gifDataHandle = TempNewHandle(4096 + 4096 * 4, &theErr);
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00000000L, theProgress.prog.progressRefCon);
if (gifDataHandle && theErr == noErr) {
HLockHi(gifDataHandle);
HLockHi(theHandle);
gGIFData.stack = (uchar *)StripAddress(*gifDataHandle);
gGIFData.tree = (ulong *)(gGIFData.stack + 4096);
imageOffset = FindNextGIFImage(theHandle, 0);
if (imageOffset != -1) {
do {
dataStart = (uchar *)StripAddress(*theHandle) + imageOffset;
if (gifGWorld = MakeGIFGWorld(theHandle, dataStart)) {
gifPixMap = GetGWorldPixMap(gifGWorld);
gGIFData.end = (uchar *)StripAddress(*theHandle) + dataLen;
theErr = DecompressGIFImage(dataStart, gifPixMap, &srcRect, &srcRect,
qd.thePort->visRgn, (ICMProgressProcRecordPtr)&theProgress);
if ((*theParams)->progress.aborted) theErr = codecAbortErr;
DisposeGWorld(gifGWorld);
} else gIntError = errNoDrawMemory, theErr = memFullErr;
if (theErr == noErr) imageOffset = FindNextGIFImage(theHandle, imageOffset);
} while (theErr == noErr && imageOffset != -1);
} else gIntError = errCorruptImage, theErr = codecBadDataErr;
HSetState(theHandle, hState);
DisposeHandle(gifDataHandle);
} else gIntError = errNoDrawMemory, theErr = memFullErr;
if (theProgress.prog.progressProc) CallICMProgressProc(theProgress.prog.progressProc,
codecProgressUpdatePercent, 0x00010000L, theProgress.prog.progressRefCon);
return theErr;
}
//=====================================================================================
// Size FindNextGIFImage(Handle theData, Size startOffset)
//=====================================================================================
// Given a handle containing a GIF image and an offset into that handle's data, this
// routine locates the next GIF image. The initial offset must either point to the
// beginning of a GIF stream, or to a previously-found GIF image.
//=====================================================================================
static Size FindNextGIFImage(Handle theData, Size startOffset)
{
uchar *theStart = (uchar *)StripAddress(*theData), *theAdr;
uchar *theEnd = theStart + GetHandleSize(theData);
Size theSize;
theAdr = theStart + startOffset;
if (*theAdr == 'G') {
// Skip over global color table and global screen descriptors here
if (theAdr[10] & 0x80) theAdr += 3 * (1 << ((theAdr[10] & 7) + 1));
theAdr += 13;
if (theAdr >= theEnd) return -1;
} else if (*theAdr == 0x2c) {
// Skip over previous image color table, descriptors, and data here
if (theAdr[9] & 0x80) theAdr += 3 * (1 << ((theAdr[9] & 7) + 1));
theAdr += 11;
while (theSize = *theAdr++) {
theAdr += theSize;
if (theAdr > theEnd) return -1;
}
} else return -1;
// Skip over any other GIF89 extensions here
while (*theAdr == 0x21) {
theAdr += 2;
while (theSize = *theAdr++) {
theAdr += theSize;
if (theAdr >= theEnd) return -1;
}
}
// If the next code is not an image descriptor, forget it
if (*theAdr != 0x2c) return -1;
// Otherwise, return the offset of the image
return (Size)(theAdr - theStart);
}
//=====================================================================================
// OSErr ExtractGIFComments(Handle theData, Handle *theComments)
//=====================================================================================
// Extracts the raw comments contained in the given GIF data into a handle.
//=====================================================================================
static OSErr ExtractGIFComments(Handle theData, Handle *theComments)
{
char hState = HGetState(theData);
uchar *theAdr = (uchar *)StripAddress(*theData);
uchar *theEnd = theAdr + GetHandleSize(theData);
Boolean isComment, bad = false;
long theSize, weird, i;
OSErr theErr = noErr;
*theComments = nil;
// Skip over global color table and global screen descriptors here
if (theAdr[10] & 0x80) theAdr += 3 * (1 << ((theAdr[10] & 7) + 1));
theAdr += 13;
// Loop over all data, including GIF89 extensions and image descriptors
while (theAdr < theEnd && theErr == noErr) {
// For extensions, skip it all if not a comment, or else accumulate the
// data into the comments handle
if (*theAdr == 0x21) {
theAdr++;
isComment = (*theAdr++ == 0xfe);
while (theSize = *theAdr++) {
if (isComment) {
for (weird = i = 0; (i < theSize) && !bad; i++) {
if (theAdr[i] >= 0x20 && theAdr[i] <= 0x7f) continue;
else if (theAdr[i] == 0x0a) theAdr[i] = 0x0d;
else if (theAdr[i] == 0x0d && theAdr[i + 1] == 0x0a)
theAdr[++i] = 0x00;
else weird++;
}
if (!bad) bad = (weird > (theSize >> 4));
if (bad) isComment = false;
else {
if (!*theComments) theErr = PtrToHand(theAdr, theComments, theSize);
else theErr = PtrAndHand(theAdr, *theComments, theSize);
}
}
theAdr += theSize;
if (theAdr >= theEnd) break;
}
// For image descriptors, simply skip over all the extra data
} else if (*theAdr == 0x2c) {
if (theAdr[9] & 0x80) theAdr += 3 * (1 << ((theAdr[9] & 7) + 1));
theAdr += 11;
while (theSize = *theAdr++) {
theAdr += theSize;
if (theAdr >= theEnd) break;
}
// Otherwise, we've hit the end; get out of here
} else break;
}
HSetState(theData, hState);
return theErr;
}
//=====================================================================================
// PaletteHandle MakeGIFPalette(uchar *theData, short theSize)
//=====================================================================================
// Creates a palette for a GIF image, substituting white and black for the brightest
// and darkest colors.
//=====================================================================================
static PaletteHandle MakeGIFPalette(uchar *theData, short theSize)
{
ushort dist, wdist = (10 * 256), bdist = (10 * 256);
ushort whitest = 256, blackest = 256, i, cur;
PaletteHandle theResult;
RGBColor theColor;
theSize = 1 << theSize;
if (!(theResult = NewPalette(256, nil, pmTolerant, 0))) return nil;
if (theSize == 256) {
uchar *color = theData;
for (i = 0; i < 256; i++) {
dist = 3 * (ushort)color[0] + 6 * (ushort)color[1] + (ushort)color[2];
if (dist < bdist) {
bdist = dist;
blackest = i;
}
dist = (10 << 8) - dist;
if (dist < wdist) {
wdist = dist;
whitest = i;
}
color += 3;
}
}
theColor.red = theColor.green = theColor.blue = 0xffff;
SetEntryColor(theResult, 0, &theColor);
theColor.red = theColor.green = theColor.blue = 0;
SetEntryColor(theResult, 1, &theColor);
for (i = 0, cur = 2; i < theSize; i++) {
if (i != blackest && i != whitest) {
theColor.red = (ushort)(*theData << 8) + *theData;
theData++;
theColor.green = (ushort)(*theData << 8) + *theData;
theData++;
theColor.blue = (ushort)(*theData << 8) + *theData;
theData++;
SetEntryColor(theResult, cur++, &theColor);
}
}
for ( ; cur < 256; cur++) {
GetEntryColor(gColorPalette[8], cur, &theColor);
SetEntryColor(theResult, cur, &theColor);
}
return theResult;
}
//=====================================================================================
// GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData)
//=====================================================================================
// Creates a suitably large offscreen GWorld for drawing bands of a GIF image into.
//=====================================================================================
static GWorldPtr MakeGIFGWorld(Handle theHandle, uchar *theData)
{
ushort theHeight = (theData[8] << 8) + theData[7];
ushort theWidth = (theData[6] << 8) + theData[5];
GWorldPtr theGWorld = nil;
CTabHandle theColors;
if (theData[9] & 0x80) theData += 10;
else theData = (uchar *)*theHandle + 13;
if (theColors = MakeGIFCTable(theData)) {
theGWorld = NewTempGWorld(theWidth, theHeight, 8, theColors);
DisposeHandle((Handle)theColors);
}
return theGWorld;
}
//=====================================================================================
// CTabHandle MakeGIFCTable(uchar *theData)
//=====================================================================================
// Creates a color table handle for the given palette.
//=====================================================================================
static CTabHandle MakeGIFCTable(uchar *theData)
{
CTabHandle theColors;
OSErr theErr = noErr;
ColorSpec *ctData;
short i;
theColors = (CTabHandle)NewHandle(sizeof(ColorTable) + 255 * sizeof(ColorSpec));
if (!theColors)
theColors = (CTabHandle)TempNewHandle(sizeof(ColorTable) + 255 * sizeof(ColorSpec), &theErr);
if (theColors && theErr == noErr) {
(*theColors)->ctSeed = GetCTSeed();
(*theColors)->ctFlags = 0x8000;
(*theColors)->ctSize = 255;
ctData = &(*theColors)->ctTable[0];
for (i = 0; i < 256; i++) {
ctData->rgb.red = (ushort)(*theData++) << 8;
ctData->rgb.green = (ushort)(*theData++) << 8;
ctData->rgb.blue = (ushort)(*theData++) << 8;
ctData++;
}
} else theColors = nil;
return theColors;
}
//=====================================================================================
// OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect,
// Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Decompresses a single image in a GIF to the appropriate destination rectangle.
//=====================================================================================
static OSErr DecompressGIFImage(uchar *theData, PixMapHandle srcPixMap, Rect *srcRect,
Rect *dstRect, RgnHandle dstRgn, ICMProgressProcRecordPtr progProc)
{
Rect imageSrcRect, imageDstRect, bandSrcRect, bandDstRect, localSrcRect;
PixMapHandle dstPixMap = GetGWorldPixMap((CGrafPtr)qd.thePort);
short bandHeight = Height(&(*srcPixMap)->bounds), rowCount = 0;
Boolean interlaced = theData[9] & 0x40, done = false;
NestedProgress theProc = (*gGIFParams)->progress;
OSErr theErr = noErr;
// calculate the source & destination rectangle for this image
imageSrcRect.top = (theData[4] << 8) + theData[3];
imageSrcRect.left = (theData[2] << 8) + theData[1];
imageSrcRect.bottom = imageSrcRect.top + (theData[8] << 8) + theData[7];
imageSrcRect.right = imageSrcRect.left + (theData[6] << 8) + theData[5];
imageDstRect = imageSrcRect;
MapRect(&imageDstRect, srcRect, dstRect);
// skip over the descriptor and local color table, if any
if (theData[9] & 0x80) theData += 3 * (1 << ((theData[9] & 7) + 1));
theData += 10;
// reset the data pointers and LZW decoder for non-interlaced images
if (!interlaced) DecodeRow(Width(&imageSrcRect), theData, nil);
// set up the progress function
(*gGIFParams)->progress.begin = FixRatio(gCurrentLines, gTotalLines);
(*gGIFParams)->progress.begin = theProc.begin +
FixMul((*gGIFParams)->progress.begin, theProc.end - theProc.begin);
(*gGIFParams)->progress.end = FixRatio(gCurrentLines + Height(&imageSrcRect), gTotalLines);
(*gGIFParams)->progress.end = theProc.begin +
FixMul((*gGIFParams)->progress.end, theProc.end - theProc.begin);
if ((*gGIFParams)->progress.prog.progressProc)
CallICMProgressProc((*gGIFParams)->progress.prog.progressProc,
codecProgressUpdatePercent, (*gGIFParams)->progress.begin,
(*gGIFParams)->progress.prog.progressRefCon);
// initialize the band source and destination rectangles
bandSrcRect = imageSrcRect;
bandSrcRect.bottom = bandSrcRect.top + bandHeight;
// now loop until we've drawn all the bands
do {
// decompress a band; for this we need the bandSrcRect relative to the
// image rect at (0,0)
localSrcRect = bandSrcRect;
OffsetRect(&localSrcRect, -imageSrcRect.left, -imageSrcRect.top);
LockPixels(srcPixMap);
if (interlaced)
done = DecodeInterlaced(theData, srcPixMap, Width(&imageSrcRect),
Height(&imageSrcRect), &localSrcRect, &rowCount, progProc);
else done = DecodeNonInterlaced(theData, srcPixMap, Width(&imageSrcRect),
Height(&imageSrcRect), &localSrcRect, &rowCount, progProc);
UnlockPixels(srcPixMap);
// calculate the appropriate destination rectangle and CopyBits there
bandDstRect = bandSrcRect;
MapRect(&bandDstRect, srcRect, dstRect);
if (!dstRgn || bandDstRect.bottom >= (*dstRgn)->rgnBBox.top) {
char sState = HGetState((Handle)srcPixMap), dState = HGetState((Handle)dstPixMap);
HLock((Handle)srcPixMap);
HLock((Handle)dstPixMap);
LockPixels(srcPixMap);
OffsetRect(&localSrcRect, -localSrcRect.left, -localSrcRect.top);
CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, &localSrcRect,
&bandDstRect, srcCopy, dstRgn);
UnlockPixels(srcPixMap);
HSetState((Handle)dstPixMap, dState);
HSetState((Handle)srcPixMap, sState);
}
if ((done && !interlaced) || (*gGIFParams)->progress.aborted ||
bandDstRect.bottom >= dstRect->bottom ||
(dstRgn && bandDstRect.bottom >= (*dstRgn)->rgnBBox.bottom)) break;
// if we hit a bad spot of data, set the errors and break out
if (!gGIFData.end && !done) {
gIntError = errCorruptImage, theErr = codecBadDataErr;
break;
}
// if we've drawn all we need to, exit prematurely
if (dstRgn && bandDstRect.top > (*dstRgn)->rgnBBox.bottom) break;
// otherwise, set up for the next band
bandSrcRect.top += bandHeight;
bandSrcRect.bottom += bandHeight;
if (bandSrcRect.bottom > imageSrcRect.bottom)
bandSrcRect.bottom = imageSrcRect.bottom;
} while (bandDstRect.top < imageDstRect.bottom);
// clean up after the progress function
if ((*gGIFParams)->progress.prog.progressProc)
CallICMProgressProc((*gGIFParams)->progress.prog.progressProc,
codecProgressUpdatePercent, (*gGIFParams)->progress.end,
(*gGIFParams)->progress.prog.progressRefCon);
(*gGIFParams)->progress.begin = theProc.begin;
(*gGIFParams)->progress.end = theProc.end;
KeepSpinning();
gCurrentLines += Height(&imageSrcRect);
// if we were aborted, signal the abort error
return theErr;
}
//=====================================================================================
// Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short iwidth,
// short iheight, Rect *sRect, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Organizes the decoding of an interlaced GIF image band.
//=====================================================================================
static Boolean DecodeInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth,
short imageHeight, Rect *srcRect, short *rowCount,
ICMProgressProcRecordPtr progProc)
{
static short gRowCount;
uchar *baseAdr = (uchar *)GetPixBaseAddr(srcPixMap);
long rowBytes = (*srcPixMap)->rowBytes & 0x3fff;
uchar *theAdr;
OSErr theErr;
short row;
// if the top of the source rectangle is 0, reset our row counter
if (!srcRect->top) gRowCount = 0;
// reset the data pointers and LZW decoder
DecodeRow(imageWidth, theData, nil);
// do the first set of interlacing (starting at row 0, every 8 rows)
for (row = 0; row < imageHeight; row += 8) {
if (row >= srcRect->top && row < srcRect->bottom) {
theAdr = baseAdr + rowBytes * (row - srcRect->top);
(*rowCount)++;
if (!(*rowCount & 63) && progProc->progressProc) {
theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight),
progProc->progressRefCon);
if (theErr == codecAbortErr) return true;
}
if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
}
// do the second set of interlacing (starting at row 4, every 8 rows)
for (row = 4; row < imageHeight; row += 8) {
if (row >= srcRect->top && row < srcRect->bottom) {
theAdr = baseAdr + rowBytes * (row - srcRect->top);
(*rowCount)++;
if (!(*rowCount & 63) && progProc->progressProc) {
theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight),
progProc->progressRefCon);
if (theErr == codecAbortErr) return true;
}
if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
}
// do the third set of interlacing (starting at row 2, every 4 rows)
for (row = 2; row < imageHeight; row += 4) {
if (row >= srcRect->top && row < srcRect->bottom) {
theAdr = baseAdr + rowBytes * (row - srcRect->top);
(*rowCount)++;
if (!(*rowCount & 63) && progProc->progressProc) {
theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight),
progProc->progressRefCon);
if (theErr == codecAbortErr) return true;
}
if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
}
// do the last set of interlacing (starting at row 1, every other row)
for (row = 1; row < imageHeight; row += 2) {
if (row >= srcRect->top && row < srcRect->bottom) {
theAdr = baseAdr + rowBytes * (row - srcRect->top);
(*rowCount)++;
if (!(*rowCount & 63) && progProc->progressProc) {
theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight),
progProc->progressRefCon);
if (theErr == codecAbortErr) return true;
}
if (DecodeRow(imageWidth, theData, theAdr) || !gGIFData.end) return true;
} else if (DecodeRow(imageWidth, theData, (uchar *)-1) || !gGIFData.end) return true;
}
return false;
}
//=====================================================================================
// Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth,
// short imageHeight, Rect *srcRect, ICMProgressProcRecordPtr progProc)
//=====================================================================================
// Organizes the decoding of an interlaced GIF image band.
//=====================================================================================
static Boolean DecodeNonInterlaced(uchar *theData, PixMapHandle srcPixMap, short imageWidth,
short imageHeight, Rect *srcRect, short *rowCount,
ICMProgressProcRecordPtr progProc)
{
#if applec
#pragma unused(srcRect)
#endif
uchar *baseAdr = (uchar *)GetPixBaseAddr(srcPixMap);
long rowBytes = (*srcPixMap)->rowBytes & 0x3fff;
short height = Height(&(*srcPixMap)->bounds);
OSErr theErr;
short row;
for (row = 0; row < height; row++) {
if (DecodeRow(imageWidth, theData, baseAdr) || !gGIFData.end) return true;
baseAdr += rowBytes;
(*rowCount)++;
if (!(*rowCount & 63) && progProc->progressProc) {
theErr = (OSErr)CallICMProgressProc(progProc->progressProc,
codecProgressUpdatePercent, FixRatio(*rowCount, imageHeight),
progProc->progressRefCon);
if (theErr == codecAbortErr) return true;
}
}
return false;
}
//=====================================================================================
// Boolean DecodeRow(short width, uchar *src, uchar *dest)
//=====================================================================================
// Decodes a single row of a GIF image.
//=====================================================================================
static Boolean DecodeRow(short width, uchar *src, uchar *dest)
{
char mmuMode = true32b;
gGIFData.width = width;
gGIFData.src = src;
gGIFData.dest = dest;
SwapMMUMode(&mmuMode);
if (dest != (uchar *)-1) DoDecodeRow(&gGIFData);
else DoDecodeDummyRow(&gGIFData);
SwapMMUMode(&mmuMode);
return gGIFData.theReturn;
}
#if !USE_ASM
//=====================================================================================
// Generic C substitutes for the assembly functions defined below.
//=====================================================================================
static void DoDecodeRow(GIFDataPtr theData)
{
#include "DecodeRow.c"
}
static void DoDecodeDummyRow(GIFDataPtr theData)
{
#include "DecodeDummyRow.c"
}
#elif THINK_C
//=====================================================================================
// Assembly language interfaces for THINK C; here we define the offsets of all fields
// within the GIFData structure, and then provide C function wrappers around asm { }
// statements that #include the actual assembly code. In MPW, we just need to compile
// GIF.a to get the assembly routines.
//=====================================================================================
#define src offsetof(GIFData, src) /* 0 */
#define dest offsetof(GIFData, dest) /* 4 */
#define stack offsetof(GIFData, stack) /* 8 */
#define theEnd offsetof(GIFData, end) /* 12 */
#define tree offsetof(GIFData, tree) /* 16 */
#define store offsetof(GIFData, store) /* 20 */
#define charSize offsetof(GIFData, charSize) /* 68 */
#define width offsetof(GIFData, width) /* 70 */
#define theReturn offsetof(GIFData, theReturn) /* 72 */
static void DoDecodeRow(GIFDataPtr theData)
{
asm {
#include "DecodeRow.a"
}
}
static void DoDecodeDummyRow(GIFDataPtr theData)
{
asm {
#include "DecodeDummyRow.a"
}
}
#endif