mirror of
https://github.com/aaronsgiles/JPEGView.git
synced 2024-06-14 12:29:33 +00:00
92bdb55672
These are the sources for the final official release of JPEGView for the Mac, back in 1994.
1 line
29 KiB
C
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
|